diff --git a/.b4-config b/.b4-config new file mode 100644 index 00000000..36aa15c3 --- /dev/null +++ b/.b4-config @@ -0,0 +1,9 @@ +# Configuration for the `b4` tool +# See https://b4.docs.kernel.org/en/latest/config.html +[b4] + send-series-to = Linux Test Project + pw-url = https://patchwork.ozlabs.org/ + pw-project = ltp + prep-perpatch-check-cmd = ./scripts/checkpatch.pl -q --terse --no-summary --mailback --showfile --no-tree --ignore CONST_STRUCT,VOLATILE,SPLIT_STRING,FILE_PATH_CHANGES + am-perpatch-check-cmd = ./scripts/checkpatch.pl -q --terse --no-summary --mailback --no-tree --ignore CONST_STRUCT,VOLATILE,SPLIT_STRING,FILE_PATH_CHANGES + diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..bbcd7072 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +Containerfile diff --git a/.mailmap b/.mailmap index 6c4b8dab..221e2295 100644 --- a/.mailmap +++ b/.mailmap @@ -1,2 +1,3 @@ Petr Vorel Petr Vorel +Xinjian Ma (Fujitsu) diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..51825da7 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,26 @@ +version: 2 + +build: + os: "ubuntu-24.04" + tools: + python: "3.12" + apt_packages: + - autoconf + - enchant-2 + - hunspell-en-us + - make + jobs: + # Doc requires to have ltp.json + pre_build: + - make autotools + - ./configure + - make -C metadata/ + +# Build from the doc/ directory with Sphinx +sphinx: + configuration: doc/conf.py + +# Explicitly set the version of Python and its requirements +python: + install: + - requirements: doc/requirements.txt diff --git a/Containerfile b/Containerfile new file mode 100644 index 00000000..227c7d39 --- /dev/null +++ b/Containerfile @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2023 SUSE LLC + +ARG PREFIX=docker.io/ +ARG DISTRO_NAME=alpine +ARG DISTRO_RELEASE=3.18 + +FROM $PREFIX$DISTRO_NAME:$DISTRO_RELEASE AS build +ARG LTPROOT=/opt/ltp +ARG DISTRO_NAME=alpine +ARG DISTRO_RELEASE=3.18 + +RUN mkdir /build +WORKDIR /build +COPY . /build +RUN ./ci/${DISTRO_NAME}.sh +RUN git clean -fdX +RUN ./build.sh -p $LTPROOT -i + +FROM $PREFIX$DISTRO_NAME:$DISTRO_RELEASE +ARG LTPROOT=/opt/ltp +ARG KIRKROOT=/opt/kirk +ARG DISTRO_NAME=alpine + +COPY --from=build /build/ci/${DISTRO_NAME}-runtime.sh $LTPROOT/runtime-deps.sh +RUN $LTPROOT/runtime-deps.sh + +COPY --from=build $LTPROOT $LTPROOT +ENV LTPROOT=$LTPROOT +ENV PATH=$LTPROOT/testcases/bin:$LTPROOT/bin:$PATH + +RUN mkdir -p $KIRKROOT +COPY --from=build /build/tools/kirk $KIRKROOT + +USER ltp diff --git a/INSTALL b/INSTALL index eb63539a..c44bb4e6 100755 --- a/INSTALL +++ b/INSTALL @@ -55,13 +55,10 @@ in the same directory where the source files reside. $ make all $ make \ "DESTDIR=$SYSROOT" \ - SKIP_IDCHECK=[0|1] \ install - Specifying DESTDIR is optional, but required when installing to a non-host sysroot, as opposed to the host system's sysroot. -- Specify SKIP_IDCHECK=1 if and when you don't want to modify /etc/{group,passwd} - on the target system's sysroot. If you get a build error, please report it to ltp@lists.linux.it with following information, @@ -95,13 +92,10 @@ items which need fixing in the LTP tree. "top_srcdir=$TOP_SRCDIR" \ "top_builddir=$OUT_OF_BUILD_TREE_DIR" \ "DESTDIR=$SYSROOT" \ - SKIP_IDCHECK=[0|1] install - Specifying DESTDIR is optional, but required when installing to a non-host sysroot, as opposed to the host system's sysroot. -- Specify SKIP_IDCHECK=1 if and when you don't want to modify /etc/{group,passwd} - on the target system's sysroot. Quick Start ----------- @@ -143,13 +137,7 @@ contributions are welcome. 3. Build and install everything, as described above. Note the minimum software requirements above before doing so. -4. The disk I/O tests can be run by executing the diskio.sh script. In order - for these tests to successfully operate a writable high-density 3.5" floppy - must be in the disk drive and a CD-ROM with more than 100Mb of data must be - in the CD-ROM drive. The corresponding tests will fail if either disk is - missing. - -5. The network tests related installation see testcases/network/README.md. +4. The network tests related installation see testcases/network/README.md. Cross compiling --------------- @@ -177,6 +165,22 @@ PKG_CONFIG_LIBDIR=/usr/lib/i386-linux-gnu/pkgconfig CFLAGS=-m32 LDFLAGS=-m32 ./c * Arch Linux PKG_CONFIG_LIBDIR=/usr/lib32/pkgconfig CFLAGS=-m32 LDFLAGS=-m32 ./configure +Kernel modules +-------------- + +LTP contains few kernel modules and tests which are using them. +These require to be built with the same kernel headers as the running kernel (SUT). +Sometimes the best way to achieve this is to compile them on the SUT. + +Due Linux Kernel Driver Interface unstability [1], error during building kernel +modules does not break the build. Make errors fatal can be done by FORCE_MODULES=1 +make variable. + +'modules', 'modules-clean' and 'modules-install' make targets are shortcuts +to build just these modules and tests. + +[1] https://docs.kernel.org/process/stable-api-nonsense.html + Android Users ------------- @@ -202,47 +206,4 @@ LDLIBS - libraries listed after objects during link, e.g. -lc, -lpthread, -lltp. For other variables and more info about the build systems see -doc/build-system-guide.txt. - -Common Issues -------------- - -Issue: When executing configure it says: - -checking for a BSD-compatible install... /usr/bin/install -c -checking whether build environment is sane... yes -checking for gawk... gawk -checking whether make sets $(MAKE)... yes -configure: error: cannot run /bin/sh ./config.sub - -Solution: You must upgrade autoconf to 0.10.2+ and m4 to 1.4.7+; config.guess and config.sub aren't necessarily generated with older revisions of the Gnu autotools chain. - -Issue: When executing make [all] it says: - - " *** No rule to make target `/$*', needed by `pan-all'. Stop." - -Solution: You must upgrade to make 3.81. Please see the Requirements section above. - -Issue: When executing make [all] it says something like: - - # ... - install -m 00644 "/scratch/ltp-dev2/ltp/include/test.h" "/scratch/ltp-install12/include/test.h" - install -m 00644 "/scratch/ltp-dev2/ltp/include/tlibio.h" "/scratch/ltp-install12/include/tlibio.h" - install -m 00644 "/scratch/ltp-dev2/ltp/include/usctest.h" "/scratch/ltp-install12/include/usctest.h" - install -m 00644 "/scratch/ltp-dev2/ltp/include/write_log.h" "/scratch/ltp-install12/include/write_log.h" - make[1]: Leaving directory `/scratch/ltp-dev2/ltp/include' - make -C lib -f "/scratch/ltp-dev2/ltp/lib/Makefile" all - make[1]: Entering directory `/scratch/ltp-dev2/ltp/lib' - " *** No rule to make target `dataascii.o', needed by `libltp.a'. Stop." # <-- the error - -Solution: You cannot build LTP with -r / --no-builtin-rules and/or - -R / --no-builtin-variables specified. LTP relies heavily on built-in - implicit rules and variables to function properly. - -Issue: When executing make (no target, 3.80), it does the following, and doesn't execute all: - - # - make -C testcases/realtime autotools - make[1]: Entering directory `/scratch/ltp/testcases/realtime' - autoheader - make[1]: Leaving directory `/scratch/ltp/testcases/realtime' +doc/developers/build_system.rst. diff --git a/Makefile b/Makefile index 53f94d9f..d47b2528 100755 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) Linux Test Project, 2009-2022 +# Copyright (c) Linux Test Project, 2009-2025 # Copyright (c) Cisco Systems Inc., 2009-2010 # Ngie Cooper, July 2009 @@ -13,7 +13,6 @@ top_srcdir ?= $(CURDIR) include $(top_srcdir)/include/mk/env_pre.mk include $(top_srcdir)/include/mk/automake.mk -include $(top_srcdir)/include/mk/gitignore.mk .SUFFIXES: .SUFFIXES: .am .default .h .in .m4 .mk @@ -25,10 +24,6 @@ vpath %.in $(top_srcdir)/include vpath %.m4 $(top_srcdir)/m4 vpath %.mk $(top_srcdir)/mk:$(top_srcdir)/mk/include -# User wants uclinux binaries? -UCLINUX ?= 0 -export UCLINUX - # CLEAN_TARGETS: Targets which exist solely in clean. # COMMON_TARGETS: Targets which exist in all, clean, and install. # INSTALL_TARGETS: Targets which exist in clean and install (contains @@ -36,11 +31,7 @@ export UCLINUX # BOOTSTRAP_TARGETS: Directories required to bootstrap out-of-build-tree # support. -# We're not using uclinux based targets (default). -ifneq ($(UCLINUX),1) COMMON_TARGETS := pan utils -INSTALL_TARGETS := doc -endif define target_to_dir_dep_mapping ifeq ($$(filter %-clean,$(1)),) # not *-clean @@ -105,8 +96,8 @@ $(filter-out include-clean,$(CLEAN_TARGETS)):: # Just like everything depends on include-all / -install, we need to get rid # of include last to ensure that things won't be monkey screwed up. Only do -# this if we're invoking clean or a subclean directly though. -ifneq ($(filter clean,$(MAKECMDGOALS)),) +# this if we're invoking clean, distclean or a subclean directly though. +ifneq ($(filter clean distclean,$(MAKECMDGOALS)),) INCLUDE_CLEAN_RDEP_SUBJECT := $(CLEAN_TARGETS) else ifneq ($(filter %clean,$(MAKECMDGOALS)),) @@ -178,6 +169,9 @@ INSTALL_TARGETS += $(addprefix $(DESTDIR)/$(bindir)/,$(BINDIR_INSTALL_SCRIPTS)) $(INSTALL_TARGETS): $(INSTALL_DIR) $(DESTDIR)/$(bindir) +.PHONY: doc +doc: metadata-all + .PHONY: check check: $(CHECK_TARGETS) @@ -194,6 +188,7 @@ ifneq ($(build),$(host)) $(error running tests on cross-compile build not supported) endif $(call _test) + $(MAKE) test-shell-loader $(MAKE) test-metadata test-c: lib-all @@ -208,17 +203,38 @@ ifneq ($(build),$(host)) endif $(call _test,-s) +test-shell-loader: lib-all +ifneq ($(build),$(host)) + $(error running tests on cross-compile build not supported) +endif + $(top_srcdir)/testcases/lib/run_tests.sh -b $(abs_builddir) + test-metadata: metadata-all - $(MAKE) -C $(abs_srcdir)/metadata/ test + $(MAKE) -C $(abs_srcdir)/metadata test + +MODULE_DIRS := $(shell \ + dirname $$(grep -l 'include.*module\.mk' $$(find $(abs_srcdir)/testcases/ -type f -name 'Makefile'))) + + +.PHONY: modules modules-clean modules-install +modules: + @$(foreach dir,$(MODULE_DIRS),\ + echo "Build $(dir)";\ + $(MAKE) -C $(dir) || exit $$?; \ +) +modules-clean: + @$(foreach dir,$(MODULE_DIRS),\ + echo "Build $(dir)";\ + $(MAKE) -C $(dir) clean || exit $$?; \ +) +modules-install: modules + @$(foreach dir,$(MODULE_DIRS),\ + echo "Build $(dir)";\ + $(MAKE) -C $(dir) install || exit $$?; \ +) ## Help .PHONY: help help: @echo "Please read the Configuration section in $(top_srcdir)/INSTALL" @exit 1 - -## Menuconfig -menuconfig: - @$(SHELL) "$(top_srcdir)/ltpmenu" - -## End misc targets. diff --git a/README.OpenSource b/README.OpenSource index e7bf380e..872519ff 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,7 +3,7 @@ "Name" : "ltp", "License" : "GPL V2.0", "License File" : "COPYING", - "Version Number" : "20230929", + "Version Number" : "20250930", "Owner" : "liuxun@huawei.com", "Upstream URL" : "https://github.com/linux-test-project/ltp", "Description" : "The LTP testsuite contains a collection of tools for testing the Linux kernel and related features." diff --git a/README.md b/README.md deleted file mode 100755 index 68f6ec89..00000000 --- a/README.md +++ /dev/null @@ -1,213 +0,0 @@ -Linux Test Project -================== - -Linux Test Project is a joint project started by SGI, OSDL and Bull developed -and maintained by IBM, Cisco, Fujitsu, SUSE, Red Hat, Oracle and others. The -project goal is to deliver tests to the open source community that validate the -reliability, robustness, and stability of Linux. - -The LTP testsuite contains a collection of tools for testing the Linux kernel -and related features. Our goal is to improve the Linux kernel and system -libraries by bringing test automation to the testing effort. Interested open -source contributors are encouraged to join. - -Project pages are located at: http://linux-test-project.github.io/ - -The latest image is always available at: -https://github.com/linux-test-project/ltp/releases - -The discussion about the project happens at LTP mailing list: -http://lists.linux.it/listinfo/ltp - -LTP mailing list is archived at: -https://lore.kernel.org/ltp/ - -IRC #ltp at: [irc.libera.chat](https://libera.chat/) - -The git repository is located at GitHub at: -https://github.com/linux-test-project/ltp - -The patchwork instance is at: -https://patchwork.ozlabs.org/project/ltp/list/ - -Warning! -======== - -**Be careful with these tests!** - -Don't run them on production systems. Growfiles, doio, and iogen in particular -stress the I/O capabilities of systems and while they should not cause problems -on properly functioning systems, they are intended to find (or cause) problems. - -Quick guide to running the tests -================================ - -If you have git, autoconf, automake, m4, pkgconf / pkg-config, libc headers, -linux kernel headers and other common development packages installed (see -INSTALL and ci/*.sh), the chances are the following will work: - -``` -$ git clone https://github.com/linux-test-project/ltp.git -$ cd ltp -$ make autotools -$ ./configure -``` - -Now you can continue either with compiling and running a single test or with -compiling and installing the whole testsuite. - -For optional library dependencies look into scripts for major distros in -`ci/` directory. You can also build whole LTP with `./build.sh` script. - -Shortcut to running a single test ---------------------------------- -If you need to execute a single test you actually do not need to compile -the whole LTP, if you want to run a syscall testcase following should work. - -``` -$ cd testcases/kernel/syscalls/foo -$ make -$ PATH=$PATH:$PWD ./foo01 -``` - -Shell testcases are a bit more complicated since these need a path to a shell -library as well as to compiled binary helpers, but generally following should -work. - -``` -$ cd testcases/lib -$ make -$ cd ../commands/foo -$ PATH=$PATH:$PWD:$PWD/../../lib/ ./foo01.sh -``` - -Open Posix Testsuite has it's own build system which needs Makefiles to be -generated first, then compilation should work in subdirectories as well. - -``` -$ cd testcases/open_posix_testsuite/ -$ make generate-makefiles -$ cd conformance/interfaces/foo -$ make -$ ./foo_1-1.run-test -``` - -Compiling and installing all testcases --------------------------------------- - -``` -$ make -$ make install -``` - -This will install LTP to `/opt/ltp`. -* If you have a problem see `INSTALL` and `./configure --help`. -* Failing that, ask for help on the mailing list or Github. - -Some tests will be disabled if the configure script can not find their build -dependencies. - -* If a test returns `TCONF` due to a missing component, check the `./configure` - output. -* If a tests fails due to a missing user or group, see the Quick Start section - of `INSTALL`. - -Running tests -------------- - -To run all the test suites - -``` -$ cd /opt/ltp -$ ./runltp -``` - -Note that many test cases have to be executed as root. - -To run a particular test suite - -``` -$ ./runltp -f syscalls -``` - -To run all tests with `madvise` in the name - -``` -$ ./runltp -f syscalls -s madvise -``` -Also see - -``` -$ ./runltp --help -``` - -Test suites (e.g. syscalls) are defined in the runtest directory. Each file -contains a list of test cases in a simple format, see doc/ltp-run-files.txt. - -Each test case has its own executable or script, these can be executed -directly - -``` -$ testcases/bin/abort01 -``` - -Some have arguments - -``` -$ testcases/bin/mesgq\_nstest -m none -``` - -The vast majority of test cases accept the -h (help) switch - -``` -$ testcases/bin/ioctl01 -h -``` - -Many require certain environment variables to be set - -``` -$ LTPROOT=/opt/ltp PATH="$PATH:$LTPROOT/testcases/bin" testcases/bin/wc01.sh -``` - -Most commonly, the path variable needs to be set and also `LTPROOT`, but there -are a number of other variables, `runltp` usually sets these for you. - -Note that all shell scripts need the `PATH` to be set. However this is not -limited to shell scripts, many C based tests need environment variables as -well. - -For more info see `doc/user-guide.txt` or online at -https://github.com/linux-test-project/ltp/wiki/User-Guidelines. - -Network tests -------------- -Network tests require certain setup, described in `testcases/network/README.md` -(online at https://github.com/linux-test-project/ltp/tree/master/testcases/network). - -Developers corner -================= - -Before you start you should read following documents: - -* `doc/test-writing-guidelines.txt` -* `doc/build-system-guide.txt` -* `doc/library-api-writing-guidelines.txt` - -There is also a step-by-step tutorial: - -* `doc/c-test-tutorial-simple.txt` - -If something is not covered there don't hesitate to ask on the LTP mailing -list. Also note that these documents are available online at: - -* https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines -* https://github.com/linux-test-project/ltp/wiki/LTP-Library-API-Writing-Guidelines -* https://github.com/linux-test-project/ltp/wiki/Build-System -* https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial - -Although we accept GitHub pull requests, the preferred way is sending patches to our mailing list. - -It's a good idea to test patches on GitHub Actions before posting to mailing -list. Our GitHub Actions setup covers various architectures and distributions in -order to make sure LTP compiles cleanly on most common configurations. -For testing you need to just push your changes to your own LTP fork on GitHub. diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..3e176bd0 --- /dev/null +++ b/README.rst @@ -0,0 +1,29 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Linux Test Project +================== + +Linux Test Project is a joint project started by SGI, OSDL and Bull developed +and maintained by SUSE, Red Hat, Fujitsu, IBM, Cisco, Oracle and others. The +project goal is to deliver tests to the open source community that validate +reliability, robustness, and stability of the Linux Kernel. + +The testing suites contain a collection of tools for testing the Linux kernel +and related features. Our goal is to improve the Linux kernel and system +libraries by bringing test automation. + +.. warning:: + + LTP tests shouldn't run in production systems. In particular, + growfiles, doio, and iogen, stress the I/O capabilities of the systems and + they are intended to find (or cause) problems. + +Some references: + +* `Documentation `_ +* `Source code `_ +* `Releases `_ +* `Mailing List `_ +* `Working patches (patchwork) `_ +* `Working patches (lore.kernel.org) `_ +* `#ltp @ libera chat `_ diff --git a/VERSION b/VERSION index e851466f..c5ef3a27 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -20230929 +20250930 diff --git a/build.sh b/build.sh index 1767cc21..47a5a7b0 100755 --- a/build.sh +++ b/build.sh @@ -138,7 +138,7 @@ install_in_tree() install_out_tree() { cd $BUILD_DIR - make $MAKE_OPTS_OUT_TREE DESTDIR="$prefix" SKIP_IDCHECK=1 install + make $MAKE_OPTS_OUT_TREE DESTDIR="$prefix" install } usage() @@ -173,13 +173,14 @@ cross cross-compile build (requires set compiler via -c switch) native native build RUN: -autotools run only 'make autotools' -configure run only 'configure' -build run only 'make' -test run only 'make test' (not supported for cross-compile build) -test-c run only 'make test-c' (not supported for cross-compile build) -test-shell run only 'make test-shell' (not supported for cross-compile build) -install run only 'make install' +autotools run only 'make autotools' +configure run only 'configure' +build run only 'make' +test run only 'make test' (not supported for cross-compile build) +test-c run only 'make test-c' (not supported for cross-compile build) +test-shell run only 'make test-shell' (not supported for cross-compile build) +test-shell-loader run only 'make test-shell-loader' (not supported for cross-compile build) +install run only 'make install' Default configure options: in-tree: $CONFIGURE_OPTS_IN_TREE @@ -206,7 +207,7 @@ while getopts "c:hio:p:r:t:" opt; do esac;; p) prefix="$OPTARG";; r) case "$OPTARG" in - autotools|configure|build|test|test-c|test-shell|install) run="$OPTARG";; + autotools|configure|build|test|test-c|test-shell|test-shell-loader|install) run="$OPTARG";; *) echo "Wrong run type '$OPTARG'" >&2; usage; exit 1;; esac;; t) case "$OPTARG" in @@ -232,7 +233,7 @@ if [ -z "$run" -o "$run" = "build" ]; then eval build_${tree}_tree fi -if [ -z "$run" -o "$run" = "test" -o "$run" = "test-c" -o "$run" = "test-shell" ]; then +if [ -z "$run" -o "$run" = "test" -o "$run" = "test-c" -o "$run" = "test-shell" -o "$run" = "test-shell-loader" ]; then if [ "$build" = "cross" ]; then echo "cross-compile build, skipping running tests" >&2 else diff --git a/ci/alpine-runtime.sh b/ci/alpine-runtime.sh new file mode 100644 index 00000000..d0e1990d --- /dev/null +++ b/ci/alpine-runtime.sh @@ -0,0 +1,20 @@ +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2023 SUSE LLC + +apk add \ + acl \ + curl \ + jq \ + keyutils \ + libaio \ + libacl \ + libcap \ + libselinux \ + libsepol \ + libtirpc \ + numactl \ + openssl \ + py3-msgpack + +adduser -D -g "Unprivileged LTP user" ltp diff --git a/ci/alpine.sh b/ci/alpine.sh index 9ae5a8d0..254f4aae 100644 --- a/ci/alpine.sh +++ b/ci/alpine.sh @@ -1,21 +1,21 @@ -#!/bin/sh -# Copyright (c) 2019-2022 Petr Vorel -set -ex +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2019-2024 Petr Vorel apk update apk add \ acl-dev \ - asciidoc \ - asciidoctor \ autoconf \ automake \ clang \ + curl \ + jq \ gcc \ git \ + acl-dev \ keyutils-dev \ libaio-dev \ - libacl \ libcap-dev \ libselinux-dev \ libsepol-dev \ @@ -25,7 +25,6 @@ apk add \ musl-dev \ numactl-dev \ openssl-dev \ - perl-json \ pkgconfig cat /etc/os-release @@ -34,7 +33,6 @@ echo "WARNING: remove unsupported tests (until they're fixed)" cd $(dirname $0)/.. rm -rfv \ testcases/kernel/syscalls/fmtmsg/fmtmsg01.c \ - testcases/kernel/syscalls/rt_tgsigqueueinfo/rt_tgsigqueueinfo01.c \ testcases/kernel/syscalls/timer_create/timer_create01.c \ testcases/kernel/syscalls/timer_create/timer_create03.c diff --git a/ci/centos.sh b/ci/centos.sh deleted file mode 100644 index 1479a43e..00000000 --- a/ci/centos.sh +++ /dev/null @@ -1 +0,0 @@ -fedora.sh \ No newline at end of file diff --git a/ci/debian.cross-compile.sh b/ci/debian.cross-compile.sh index 0a7ef771..95cf11da 100644 --- a/ci/debian.cross-compile.sh +++ b/ci/debian.cross-compile.sh @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2018-2020 Petr Vorel -set -ex if [ -z "$ARCH" ]; then echo "missing \$ARCH!" >&2 diff --git a/ci/debian.i386.sh b/ci/debian.i386.sh index 707a23ca..44c7ddf2 100644 --- a/ci/debian.i386.sh +++ b/ci/debian.i386.sh @@ -1,20 +1,22 @@ -#!/bin/sh -# Copyright (c) 2018-2020 Petr Vorel -set -ex +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018-2024 Petr Vorel dpkg --add-architecture i386 apt update apt install -y --no-install-recommends \ + curl \ + jq \ linux-libc-dev:i386 \ gcc-multilib \ - libacl1:i386 \ - libaio1:i386 \ - libcap2:i386 \ + libacl1-dev:i386 \ + libaio-dev:i386 \ + libcap-dev:i386 \ libc6-dev-i386 \ libc6:i386 \ - libkeyutils1:i386 \ - libnuma1:i386 \ + libkeyutils-dev:i386 \ + libnuma-dev:i386 \ libssl-dev:i386 \ libtirpc-dev:i386 \ pkg-config:i386 diff --git a/ci/debian.minimal.sh b/ci/debian.minimal.sh index b51154b0..1e8dd19a 100644 --- a/ci/debian.minimal.sh +++ b/ci/debian.minimal.sh @@ -1,21 +1,5 @@ -#!/bin/sh -# Copyright (c) 2018-2023 Petr Vorel -set -ex +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018-2024 Petr Vorel -apt="apt remove -y" - -$apt \ - asciidoc \ - asciidoctor \ - libacl1-dev \ - libaio-dev \ - libaio1 \ - libcap-dev \ - libkeyutils-dev \ - libnuma-dev \ - libnuma1 \ - libselinux1-dev \ - libsepol-dev \ - libssl-dev - -$apt asciidoc-base ruby-asciidoctor || true +ACTION="remove-nonessential" $(dirname $0)/debian.sh diff --git a/ci/debian.sh b/ci/debian.sh index da92337f..0445c92e 100644 --- a/ci/debian.sh +++ b/ci/debian.sh @@ -1,6 +1,6 @@ -#!/bin/sh -# Copyright (c) 2018-2021 Petr Vorel -set -ex +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018-2024 Petr Vorel # workaround for missing oldstable-updates repository # W: Failed to fetch http://deb.debian.org/debian/dists/oldstable-updates/main/binary-amd64/Packages @@ -11,44 +11,56 @@ apt update # workaround for Ubuntu impish asking to interactively configure tzdata export DEBIAN_FRONTEND="noninteractive" -apt="apt install -y --no-install-recommends" +install="apt install -y --no-install-recommends" +remove="apt remove -y" -$apt \ - acl-dev \ - asciidoc \ - asciidoctor \ - autoconf \ - automake \ - build-essential \ - debhelper \ - devscripts \ - clang \ - gcc \ - git \ - iproute2 \ - libacl1 \ - libacl1-dev \ - libaio-dev \ - libaio1 \ - libcap-dev \ - libcap2 \ - libc6 \ - libc6-dev \ - libjson-perl \ - libkeyutils-dev \ - libkeyutils1 \ - libmnl-dev \ - libnuma-dev \ - libnuma1 \ - libselinux1-dev \ - libsepol-dev \ - libssl-dev \ - libtirpc-dev \ - linux-libc-dev \ - lsb-release \ +# libc6-dev and libtirpc-dev are hard dependencies for gcc toolchain +# LTP should be compilable without linux-libc-dev, but we expect kernel headers. +pkg_minimal=" + autoconf + automake + build-essential + debhelper + devscripts + clang + curl + jq + gcc + git + iproute2 + libc6-dev + libtirpc-dev + linux-libc-dev + lsb-release pkg-config +" -$apt ruby-asciidoctor-pdf || true -$apt asciidoc-dblatex || true +pkg_nonessential=" + acl-dev + libacl1-dev + libaio-dev + libcap-dev + libkeyutils-dev + libnuma-dev + libmnl-dev + libselinux1-dev + libsepol-dev + libssl-dev +" + +case "$ACTION" in + minimal) + echo "=== Installing only minimal dependencies ===" + $install $pkg_minimal + ;; + remove-nonessential) + echo "=== Make sure devel libraries are removed ===" + $remove $pkg_nonessential + ;; + *) + echo "=== Installing dependencies ===" + $install $pkg_minimal $pkg_nonessential + ;; +esac df -hT diff --git a/ci/fedora.sh b/ci/fedora.sh index a603bcbe..f57806eb 100644 --- a/ci/fedora.sh +++ b/ci/fedora.sh @@ -1,15 +1,23 @@ -#!/bin/sh +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2018-2021 Petr Vorel -set -ex -yum="yum -y install --skip-broken" +if command -v dnf5 >/dev/null 2>&1; then + yum="dnf5 -y install --skip-broken --skip-unavailable" +elif command -v dnf >/dev/null 2>&1; then + yum="dnf -y install --skip-broken" +else + yum="yum -y install --skip-broken" +fi $yum \ - asciidoc \ autoconf \ automake \ make \ clang \ + gawk \ + curl \ + jq \ gcc \ git \ findutils \ @@ -17,11 +25,8 @@ $yum \ numactl-devel \ libtirpc \ libtirpc-devel \ - perl-JSON \ - perl-libwww-perl \ pkg-config \ redhat-lsb-core # CentOS 8 fixes $yum libmnl-devel || $yum libmnl -$yum rubygem-asciidoctor || true diff --git a/ci/opensuse.sh b/ci/opensuse.sh index 11c5f4b6..8a30b02c 100644 --- a/ci/opensuse.sh +++ b/ci/opensuse.sh @@ -1 +1,46 @@ -tumbleweed.sh \ No newline at end of file +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018-2025 Petr Vorel + +zyp="zypper --non-interactive install --force-resolution --no-recommends" + +i=10 +while [ $i != 0 ]; do + $zyp \ + autoconf \ + automake \ + clang \ + curl \ + jq \ + findutils \ + gcc \ + git \ + gzip \ + iproute2 \ + make \ + kernel-default-devel \ + keyutils-devel \ + libacl-devel \ + libaio-devel \ + libcap-devel \ + libmnl-devel \ + libnuma-devel \ + libopenssl-devel \ + libselinux-devel \ + libtirpc-devel \ + linux-glibc-devel \ + lsb-release \ + pkg-config + + ret=$? + + if [ $ret -eq 106 ]; then + echo "Broken repositories, try $i..." + sleep 5 + i=$((i-1)) + continue + fi + break +done + +exit $ret diff --git a/ci/quay.io.sh b/ci/quay.io.sh new file mode 100644 index 00000000..f57806eb --- /dev/null +++ b/ci/quay.io.sh @@ -0,0 +1,32 @@ +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018-2021 Petr Vorel + +if command -v dnf5 >/dev/null 2>&1; then + yum="dnf5 -y install --skip-broken --skip-unavailable" +elif command -v dnf >/dev/null 2>&1; then + yum="dnf -y install --skip-broken" +else + yum="yum -y install --skip-broken" +fi + +$yum \ + autoconf \ + automake \ + make \ + clang \ + gawk \ + curl \ + jq \ + gcc \ + git \ + findutils \ + iproute \ + numactl-devel \ + libtirpc \ + libtirpc-devel \ + pkg-config \ + redhat-lsb-core + +# CentOS 8 fixes +$yum libmnl-devel || $yum libmnl diff --git a/ci/tools/patchwork.sh b/ci/tools/patchwork.sh new file mode 100644 index 00000000..3e18ee94 --- /dev/null +++ b/ci/tools/patchwork.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Shell script to communicate with Patchwork via REST API. +# It has been mainly created for CI purposes, but it can be used in the shell +# by satisfying minimum requirements. +# +# Copyright (c) 2025 Andrea Cervesato + +PATCHWORK_URL="${PATCHWORK_URL:-https://patchwork.ozlabs.org}" +PATCHWORK_SINCE="${PATCHWORK_SINCE:-3600}" + +command_exists() { + local cmd + + for cmd in "$@"; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "'$1' must be present in the system" >&2 + exit 1 + fi + done +} + +command_exists "curl" "jq" + +fetch_series() { + local current_time=$(date +%s) + local since_time=$(expr $current_time - $PATCHWORK_SINCE) + local date=$(date -u -d @$since_time +"%Y-%m-%dT%H:%M:%SZ") + local stdout + + stdout=$(curl -k -G "$PATCHWORK_URL/api/events/" \ + --data "category=series-completed" \ + --data "project=ltp" \ + --data "state=new" \ + --data "since=$date" \ + --data "archive=no") + + [ $? -eq 0 ] || exit 1 + + echo "$stdout" | jq -r '.[] | "\(.payload.series.id) \(.payload.series.mbox)"' +} + +get_patches() { + local series_id="$1" + local stdout + + stdout="$(curl -k -G $PATCHWORK_URL/api/patches/ \ + --data project=ltp \ + --data series=$series_id)" + + [ $? -eq 0 ] || exit 1 + + echo "$stdout" | jq -r '.[] | "\(.id)"' +} + +verify_token_exists() { + if [ -z "$PATCHWORK_TOKEN" ]; then + echo "For this feature you need \$PATCHWORK_TOKEN" + exit 1 + fi +} + +set_patch_state() { + local patch_id="$1" + local state="$2" + + verify_token_exists + + curl -k -X PATCH \ + -H "Authorization: Token $PATCHWORK_TOKEN" \ + -F "state=$state" \ + "$PATCHWORK_URL/api/patches/$patch_id/" + + [ $? -eq 0 ] || exit 1 +} + +set_series_state() { + if [ $# -ne 2 ]; then + echo "'state' command expects 2 parameters ($#)" + exit 1 + fi + + local series_id="$1" + local state="$2" + + get_patches "$series_id" | while read -r patch_id; do + if [ "$patch_id" ]; then + set_patch_state "$patch_id" "$state" + fi + done +} + +get_checks() { + local patch_id="$1" + local stdout + + stdout="$(curl -k -G $PATCHWORK_URL/api/patches/$patch_id/checks/)" + + [ $? -eq 0 ] || exit 1 + + echo "$stdout" | jq -r '.[] | "\(.id)"' +} + +already_tested() { + local series_id="$1" + + get_patches "$series_id" | while read -r patch_id; do + [ "$patch_id" ] || continue + + get_checks "$patch_id" | while read -r check_id; do + if [ -n "$check_id" ]; then + echo "$check_id" + return + fi + done + done +} + +verify_new_patches() { + local tmp=$(mktemp -d) + local output="$tmp/series_ids.txt" + + touch "$output" + + fetch_series | while read -r series_id series_mbox; do + [ "$series_id" ] || continue + + tested=$(already_tested "$series_id") + [ "$tested" ] && continue + + echo "$series_id|$series_mbox" >>"$output" + done + + cat "$output" +} + +send_results() { + if [ $# -ne 4 -a $# -ne 5 ]; then + echo "'check' command expects 4 or 5 parameters ($#)" + exit 1 + fi + + local series_id="$1" + local target_url="$2" + + verify_token_exists + + local context=$(echo "$3" | sed 's/:/_/g; s/\//-/g; s/\./-/g') + + [ "$CC" ] && context="${context}_${CC}" + [ "$ARCH" ] && context="${context}_${ARCH}" + + local result="$4" + [ "$result" = "cancelled" ] && return + + local description="${5:-$result}" + + local state="fail" + + # patchwork REST API allowed states: pending, success, warning, fail. + # https://github.com/getpatchwork/patchwork/blob/main/docs/api/schemas/v1.2/patchwork.yaml#L708 + # https://patchwork.readthedocs.io/en/latest/api/rest/schemas/v1.2/#get--api-1.2-patches-patch_id-checks + case "$result" in + success|pending) state=$result;; + esac + + get_patches "$series_id" | while read -r patch_id; do + [ "$patch_id" ] || continue + + curl -k -X POST \ + -H "Authorization: Token $PATCHWORK_TOKEN" \ + -F "state=$state" \ + -F "context=$context" \ + -F "target_url=$target_url" \ + -F "description=$description" \ + "$PATCHWORK_URL/api/patches/$patch_id/checks/" + + [ $? -eq 0 ] || exit 1 + done +} + +case "$1" in +state) + set_series_state "$2" "$3" + ;; +check) + send_results "$2" "$3" "$4" "$5" "$6" + ;; +verify) + verify_new_patches + ;; +*) + echo "Available commands: state, check, verify" >&2 + exit 1 + ;; +esac diff --git a/ci/tumbleweed-runtime.sh b/ci/tumbleweed-runtime.sh new file mode 100644 index 00000000..3eea991a --- /dev/null +++ b/ci/tumbleweed-runtime.sh @@ -0,0 +1,15 @@ +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2023 SUSE LLC + +zyp="zypper --non-interactive install --force-resolution --no-recommends" + +$zyp \ + iproute2 \ + keyutils \ + libaio1 \ + libmnl0 \ + libnuma1 \ + libtirpc3 + +useradd ltp diff --git a/ci/tumbleweed.sh b/ci/tumbleweed.sh index f1e7252f..8a30b02c 100644 --- a/ci/tumbleweed.sh +++ b/ci/tumbleweed.sh @@ -1,33 +1,46 @@ -#!/bin/sh -# Copyright (c) 2018-2021 Petr Vorel -set -ex +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018-2025 Petr Vorel zyp="zypper --non-interactive install --force-resolution --no-recommends" -$zyp \ - asciidoc \ - autoconf \ - automake \ - clang \ - findutils \ - gcc \ - git \ - gzip \ - iproute2 \ - make \ - kernel-default-devel \ - keyutils-devel \ - libacl-devel \ - libaio-devel \ - libcap-devel \ - libmnl-devel \ - libnuma-devel \ - libopenssl-devel \ - libselinux-devel \ - libtirpc-devel \ - linux-glibc-devel \ - lsb-release \ - perl-JSON \ - pkg-config +i=10 +while [ $i != 0 ]; do + $zyp \ + autoconf \ + automake \ + clang \ + curl \ + jq \ + findutils \ + gcc \ + git \ + gzip \ + iproute2 \ + make \ + kernel-default-devel \ + keyutils-devel \ + libacl-devel \ + libaio-devel \ + libcap-devel \ + libmnl-devel \ + libnuma-devel \ + libopenssl-devel \ + libselinux-devel \ + libtirpc-devel \ + linux-glibc-devel \ + lsb-release \ + pkg-config -$zyp ruby2.7-rubygem-asciidoctor || $zyp ruby2.5-rubygem-asciidoctor || true + ret=$? + + if [ $ret -eq 106 ]; then + echo "Broken repositories, try $i..." + sleep 5 + i=$((i-1)) + continue + fi + break +done + +exit $ret diff --git a/ci/ubuntu.sh b/ci/ubuntu.sh index 0edcb8b8..0445c92e 100644 --- a/ci/ubuntu.sh +++ b/ci/ubuntu.sh @@ -1 +1,66 @@ -debian.sh \ No newline at end of file +#!/bin/sh -eux +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018-2024 Petr Vorel + +# workaround for missing oldstable-updates repository +# W: Failed to fetch http://deb.debian.org/debian/dists/oldstable-updates/main/binary-amd64/Packages +grep -v oldstable-updates /etc/apt/sources.list > /tmp/sources.list && mv /tmp/sources.list /etc/apt/sources.list + +apt update + +# workaround for Ubuntu impish asking to interactively configure tzdata +export DEBIAN_FRONTEND="noninteractive" + +install="apt install -y --no-install-recommends" +remove="apt remove -y" + +# libc6-dev and libtirpc-dev are hard dependencies for gcc toolchain +# LTP should be compilable without linux-libc-dev, but we expect kernel headers. +pkg_minimal=" + autoconf + automake + build-essential + debhelper + devscripts + clang + curl + jq + gcc + git + iproute2 + libc6-dev + libtirpc-dev + linux-libc-dev + lsb-release + pkg-config +" + +pkg_nonessential=" + acl-dev + libacl1-dev + libaio-dev + libcap-dev + libkeyutils-dev + libnuma-dev + libmnl-dev + libselinux1-dev + libsepol-dev + libssl-dev +" + +case "$ACTION" in + minimal) + echo "=== Installing only minimal dependencies ===" + $install $pkg_minimal + ;; + remove-nonessential) + echo "=== Make sure devel libraries are removed ===" + $remove $pkg_nonessential + ;; + *) + echo "=== Installing dependencies ===" + $install $pkg_minimal $pkg_nonessential + ;; +esac + +df -hT diff --git a/configure.ac b/configure.ac index 662c4c05..0480f46c 100755 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,14 @@ +# Copyright (c) Linux Test Project, 2008-2025 +# SPDX-License-Identifier: GPL-2.0-or-later + AC_PREREQ(2.64) AC_INIT([ltp], [LTP_VERSION], [ltp@lists.linux.it]) AC_CONFIG_AUX_DIR([.]) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([include/config.h]) AC_CONFIG_MACRO_DIR([m4]) + +# Adding files here? Please update include/mk/automake.mk AC_CONFIG_FILES([ \ include/mk/config.mk \ include/mk/config-openposix.mk \ @@ -27,6 +32,7 @@ AC_PROG_RANLIB AC_DEFUN([AC_PROG_STRIP], [AC_CHECK_TOOL(STRIP, strip, :)]) AC_PROG_STRIP AC_PROG_YACC +AC_CHECK_TOOL([OBJCOPY], [objcopy], [:]) m4_ifndef([PKG_CHECK_EXISTS], [m4_fatal([must install pkg-config or pkgconfig and pkg.m4 macro (usual dependency), see INSTALL])]) @@ -34,16 +40,22 @@ m4_ifndef([PKG_CHECK_EXISTS], AC_PREFIX_DEFAULT(/opt/ltp) AC_CHECK_DECLS([IFLA_NET_NS_PID],,,[#include ]) +AC_CHECK_DECLS([LANDLOCK_RULE_PATH_BENEATH],,,[#include ]) +AC_CHECK_DECLS([LANDLOCK_RULE_NET_PORT],,,[#include ]) AC_CHECK_DECLS([MADV_MERGEABLE],,,[#include ]) +AC_CHECK_DECLS([NFTA_CHAIN_ID, NFTA_VERDICT_CHAIN_ID],,,[#include ]) AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ],,,[#include ]) AC_CHECK_DECLS([SEM_STAT_ANY],,,[#include ]) AC_CHECK_HEADERS_ONCE([ \ + aio.h \ asm/ldt.h \ + asm/prctl.h \ cpuid.h \ emmintrin.h \ ifaddrs.h \ keyutils.h \ + linux/blkdev.h \ linux/can.h \ linux/cgroupstats.h \ linux/cryptouser.h \ @@ -52,18 +64,22 @@ AC_CHECK_HEADERS_ONCE([ \ linux/fs.h \ linux/futex.h \ linux/genetlink.h \ + linux/genhd.h \ linux/if_alg.h \ linux/if_ether.h \ linux/if_packet.h \ linux/io_uring.h \ linux/ioprio.h \ linux/keyctl.h \ + linux/landlock.h \ + linux/lsm.h \ linux/mempolicy.h \ linux/module.h \ linux/mount.h \ linux/netlink.h \ linux/seccomp.h \ linux/securebits.h \ + linux/tls.h \ linux/tty.h \ linux/types.h \ linux/userfaultfd.h \ @@ -74,6 +90,7 @@ AC_CHECK_HEADERS_ONCE([ \ sys/inotify.h \ sys/pidfd.h sys/prctl.h \ + sys/random.h \ sys/shm.h \ sys/timerfd.h \ sys/ustat.h \ @@ -86,6 +103,7 @@ AC_SUBST(HAVE_FTS_H, $have_fts) AC_CHECK_HEADERS(linux/vm_sockets.h, [], [], [#include ]) AC_CHECK_FUNCS_ONCE([ \ + cachestat \ clone3 \ close_range \ copy_file_range \ @@ -94,7 +112,8 @@ AC_CHECK_FUNCS_ONCE([ \ execveat \ faccessat2 \ fallocate \ - fchownat \ + file_getattr \ + file_setattr \ fsconfig \ fsmount \ fsopen \ @@ -113,8 +132,6 @@ AC_CHECK_FUNCS_ONCE([ \ mallinfo \ mallinfo2 \ mallopt \ - mkdirat \ - mknodat \ modify_ldt \ mount_setattr \ move_mount \ @@ -133,9 +150,7 @@ AC_CHECK_FUNCS_ONCE([ \ pwritev2 \ quotactl_fd \ rand_r \ - readlinkat \ recvmmsg \ - renameat \ renameat2 \ sched_getcpu \ sendmmsg \ @@ -174,20 +189,29 @@ AC_CHECK_TYPES([enum kcmp_type],,,[#include ]) AC_CHECK_TYPES([struct acct_v3],,,[#include ]) AC_CHECK_TYPES([struct af_alg_iv, struct sockaddr_alg],,,[# include ]) AC_CHECK_TYPES([struct fanotify_event_info_fid, struct fanotify_event_info_error, - struct fanotify_event_info_header, struct fanotify_event_info_pidfd],,,[#include ]) + struct fanotify_event_info_header, struct fanotify_event_info_pidfd, + struct fanotify_event_info_range],,,[#include ]) +AC_CHECK_TYPES([struct file_clone_range],,,[#include ]) AC_CHECK_TYPES([struct file_dedupe_range],,,[#include ]) +AC_CHECK_TYPES([struct procmap_query],,,[#include ]) AC_CHECK_TYPES([struct file_handle],,,[ #define _GNU_SOURCE #include ]) -AC_CHECK_TYPES([struct fs_quota_statv],,,[#include ]) +AC_CHECK_TYPES([struct fs_quota_statv],,,[ +#define _GNU_SOURCE +#include +]) + AC_CHECK_TYPES([struct if_nextdqblk],,,[#include ]) AC_CHECK_TYPES([struct iovec],,,[#include ]) AC_CHECK_TYPES([struct ipc64_perm],,,[#include ]) AC_CHECK_TYPES([struct loop_config],,,[#include ]) - +AC_CHECK_TYPES([struct landlock_path_beneath_attr],,,[#include ]) +AC_CHECK_TYPES([struct landlock_net_port_attr],,,[#include ]) +AC_CHECK_TYPES([struct lsm_ctx],,,[#include ]) AC_CHECK_TYPES([struct mmsghdr],,,[ #define _GNU_SOURCE #include @@ -222,7 +246,8 @@ AC_CHECK_TYPES([struct xt_entry_match, struct xt_entry_target],,,[ ]) AC_CHECK_TYPES([struct __kernel_old_timeval, struct __kernel_old_timespec, struct __kernel_timespec, - struct __kernel_old_itimerspec, struct __kernel_itimerspec],,,[#include ]) + struct __kernel_old_itimerspec, struct __kernel_itimerspec, + struct __kernel_old_itimerval],,,[#include ]) AC_CHECK_TYPES([struct futex_waitv],,,[#include ]) AC_CHECK_TYPES([struct mount_attr],,,[ @@ -233,6 +258,25 @@ AC_CHECK_TYPES([struct mount_attr],,,[ #endif ]) +AC_CHECK_TYPES([struct cachestat_range],,,[#include ]) +AC_CHECK_TYPES([struct cachestat],,,[#include ]) + +# Defined in , but include/lapi/mount.h includes */ +AC_CHECK_TYPES([struct mnt_id_req],,,[#include ]) +AC_CHECK_TYPES([struct statmount],,,[#include ]) +AC_CHECK_MEMBERS([struct statmount.mnt_ns_id],,,[#include +#include ]) + +AC_CHECK_TYPES([struct pidfd_info],,,[#include ]) +AC_CHECK_TYPES([struct file_attr],,,[#include ]) + +AC_CHECK_TYPES([struct fsxattr],,,[#include ]) + +AC_CHECK_TYPES([struct sockaddr_vm],,,[ +#include +#include +]) + # Tools knobs # Bash @@ -248,33 +292,6 @@ else AC_SUBST([WITH_BASH],["no"]) fi -# metadata -AC_ARG_ENABLE([metadata], - [AS_HELP_STRING([--disable-metadata], - [Disable metadata generation (both HTML and PDF, default no)])], - [], [enable_metadata=yes] -) -AC_ARG_ENABLE([metadata_html], - [AS_HELP_STRING([--disable-metadata-html], - [Disable metadata HTML generation (default no)])], - [], [enable_metadata_html=yes] -) - -AC_ARG_ENABLE([metadata_pdf], - [AS_HELP_STRING([--enable-metadata-pdf], - [Enable metadata PDF generation (default no)])], - [], [enable_metadata_pdf=no] -) - -AC_ARG_WITH([metadata_generator], - [AS_HELP_STRING([--with-metadata-generator=asciidoc|asciidoctor], - [Specify metadata generator to use (default autodetect)])], - [with_metadata_generator=$withval], - [with_metadata_generator=detect] -) - -LTP_DOCPARSE - # Expect AC_ARG_WITH([expect], [AS_HELP_STRING([--with-expect], @@ -359,20 +376,18 @@ fi # TODO: testcases/realtime requires bash and python. AC_ARG_WITH([realtime-testsuite], [AS_HELP_STRING([--with-realtime-testsuite], - [compile and install the realtime testsuite])], + [unused, kept for compatibility reason])], [with_realtime_testsuite=$withval], [with_realtime_testsuite=no] ) if test "x$with_realtime_testsuite" = xyes; then AC_SUBST([WITH_REALTIME_TESTSUITE],["yes"]) - # Run configure on testcases/realtime as well. - AC_CONFIG_SUBDIRS([testcases/realtime]) else AC_SUBST([WITH_REALTIME_TESTSUITE],["no"]) fi -AC_CONFIG_COMMANDS([syscalls.h], [cd ${ac_top_srcdir}/include/lapi/syscalls; ./regen.sh]) +AC_CONFIG_COMMANDS([syscalls.h], [cd ${ac_top_srcdir}/include/lapi/syscalls; ./generate_syscalls.sh ../syscalls.h; cd - >/dev/null]) # custom functions # NOTE: don't create custom functions for simple checks, put them into this file @@ -386,17 +401,15 @@ LTP_CHECK_FORTIFY_SOURCE LTP_CHECK_KERNEL_DEVEL LTP_CHECK_KEYUTILS_SUPPORT LTP_CHECK_LIBMNL -LTP_CHECK_LINUX_PTRACE -LTP_CHECK_LINUXRANDOM -LTP_CHECK_NOMMU_LINUX LTP_CHECK_SELINUX LTP_CHECK_SYNC_ADD_AND_FETCH LTP_CHECK_SYSCALL_EVENTFD -LTP_CHECK_SYSCALL_FCNTL LTP_CHECK_FSVERITY AX_CHECK_COMPILE_FLAG([-no-pie], [LTP_CFLAGS_NOPIE=1]) +AX_CHECK_COMPILE_FLAG([-ffixed-ebp], [LTP_CFLAGS_FFIXED_EBP=1]) AC_SUBST([LTP_CFLAGS_NOPIE]) +AC_SUBST([LTP_CFLAGS_FFIXED_EBP]) if test "x$with_numa" = xyes; then LTP_CHECK_SYSCALL_NUMA @@ -407,7 +420,6 @@ fi AC_DEFINE_UNQUOTED(NUMA_ERROR_MSG, ["$numa_error_msg"], [Error message when no NUMA support]) -LTP_CHECK_SYSCALL_SIGNALFD LTP_CHECK_SYSCALL_UTIMENSAT LTP_CHECK_TASKSTATS test "x$with_tirpc" = xyes && LTP_CHECK_TIRPC @@ -422,10 +434,12 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM()], [ AC_MSG_RESULT([yes]) AC_SUBST([WITH_KVM_TESTSUITE],["yes"]) + have_kvm=yes ], [ AC_MSG_RESULT([no]) AC_SUBST([WITH_KVM_TESTSUITE],["no"]) + have_kvm=no ]) _AC_LANG_PREFIX[]FLAGS="$ltp_backup_flags" LDFLAGS="$ltp_backup_ldflags" @@ -435,8 +449,10 @@ AC_OUTPUT cat << EOF TESTSUITES +kernel modules: $WITH_MODULES (kernel: $LINUX_VERSION) +KVM testsuite: $have_kvm open posix testsuite: ${with_open_posix_testsuite:-no} -realtime testsuite: ${with_realtime_testsuite:-no} +TI-RPC testsuite: ${with_tirpc:-yes} LIBRARIES keyutils: ${have_keyutils:-yes} @@ -448,9 +464,8 @@ libmnl: ${have_libmnl:-yes} libnuma: ${have_libnuma:-no} (headers: ${have_numa_headers:-yes}, v2 headers: ${have_numa_headers_v2:-no}) libtirpc: ${have_libtirpc:-no} glibc SUN-RPC: ${have_rpc_glibc:-no} - -METADATA -metadata generator: $with_metadata_generator -HTML metadata: $with_metadata_html -PDF metadata: $with_metadata_pdf EOF + +if test "x$with_realtime_testsuite" = xyes; then + AC_MSG_WARN([--with-realtime-testsuite has no effect and is kept for compatibilty reason. It will be removed in the future.]) +fi diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..d84656f3 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,6 @@ +.venv/ +html/ +build/ +_static/syscalls.rst +_static/tests.rst +syscalls.tbl diff --git a/doc/Build-System.rest b/doc/Build-System.rest deleted file mode 100644 index e078fa7f..00000000 --- a/doc/Build-System.rest +++ /dev/null @@ -1,218 +0,0 @@ -Short introduction into LTP build system -======================================== - -****************************************************************************** -The following document briefly describes the steps and methodologies used for -the new and improved Makefile system. - -Changelog: - - * Initial version: Ngie Cooper - * Reformated for asciidoc: Cyril Hrubis -****************************************************************************** - -The Problem ------------ - -The problem with the old Makefile system is that it was very difficult to -maintain and it lacked any sense of formal structure, thus developing for LTP -and including new targets was more difficult than it should have been -(maintenance). Furthermore, proper option-based cross-compilation was -impossible due to the fact that the Makefiles didn't support a prefixing -system, and the appropriate implicit / static rules hadn't been configured to -compile into multiple object directories for out-of-tree build support (ease of -use / functionality). Finally, there wasn't a means to setup dependencies -between components, such that if a component required libltp.a in order to -compile, it would go off and compile libltp.a first (ease of use). - -These items needed to be fixed to reduce maintenance nightmares for the -development community contributing to LTP, and the project maintainers. - -Design ------- - -The system was designed such that including a single GNU Makefile compatible -set in each new directory component is all that's essentially required to -build the system. - -Say you had a directory like the following (with .c files in them which -directly tie into applications, e.g. baz.c -> baz): - -------------------------------------------------------------------------------- -.../foo/ - |--> Makefile - | - --> bar/ - | - --> Makefile - | - --> baz.c -------------------------------------------------------------------------------- - -Here's an example of how one would accomplish that: - -------------------------------------------------------------------------------- -.../foo/Makefile: -# -# Copyright disclaimer goes here -- please use GPLv2. -# - -top_srcdir ?= .. - -include $(top_srcdir)/include/mk/env_pre.mk -include $(top_srcdir)/include/mk/generic_trunk_target.mk - -.../foo/bar/Makefile: -# -# Copyright disclaimer goes here -- please use GPLv2. -# - -top_srcdir ?= .. - -include $(top_srcdir)/include/mk/env_pre.mk -include $(top_srcdir)/include/mk/generic_leaf_target.mk -------------------------------------------------------------------------------- - -Kernel Modules --------------- - -Some of the tests need to build kernel modules, happily LTP has -infrastructure for this. - -------------------------------------------------------------------------------- -ifneq ($(KERNELRELEASE),) - -obj-m := module01.o - -else - -top_srcdir ?= ../../../.. -include $(top_srcdir)/include/mk/testcases.mk - -REQ_VERSION_MAJOR := 2 -REQ_VERSION_PATCH := 6 -MAKE_TARGETS := test01 test02 module01.ko - -include $(top_srcdir)/include/mk/module.mk -include $(top_srcdir)/include/mk/generic_leaf_target.mk - -endif -------------------------------------------------------------------------------- - -This is example Makefile that allows you build kernel modules inside of LTP. -The prerequisites for the build are detected by the 'configure' script. - -The 'REQ_VERSION_MAJOR' and 'REQ_VERSION_PATCH' describe minimal kernel -version for which the build system tries to build the module. - -The buildsystem is also forward compatible with changes in Linux kernel -internal API so that if module fails to build the failure is ignored both on -build and installation. If the userspace counterpart of the test fails to load -the module because the file does not exists, the test is skipped. - -Note the 'ifneq($(KERNELRELEASE),)', the reason it's there is that the -Makefile is executed twice, once by LTP build system and once by kernel -kbuild, see 'Documentation/kbuild/modules.txt' in the Linux kernel tree for -details on external module build. - -Make Rules and Make Variables ------------------------------ - -When using make rules, avoid writing ad hoc rules like: - -------------------------------------------------------------------------------- -[prog]: [dependencies] - cc -I../../include $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(LDLIBS) \ - -o [prog] [dependencies] -------------------------------------------------------------------------------- - -etc. This makes cross-compilation and determinism difficult, if not impossible. -Besides, implicit rules are your friends and as long as you use `MAKEOPTS=;' in -the top-level caller (or do $(subst r,$(MAKEOPTS)) to remove -r), the compile -will complete successfully, assuming all other prerequisites have been -fulfilled (libraries, headers, etc). - -------------------------------------------------------------------------------- -$(AR) : The library archiver. - -$(CC) : The system C compiler. - -$(CPP) : The system C preprocessor. - -$(CFLAGS) : C compiler flags. - -$(CPPFLAGS) : Preprocessor flags, e.g. -I arguments. - -$(DEBUG_CFLAGS) : Debug flags to pass to $(CC), -g, etc. - -$(KVM_LD) : Special linker for wrapping KVM payload binaries - into linkable object files. Defaults to $(LD). - Change this variable if the KVM Makefile fails - to build files named *-payload.o. - -$(LD) : The system linker (typically $(CC), but not - necessarily). - -$(LDFLAGS) : What to pass in to the linker, including -L arguments - and other ld arguments, apart from -l library - includes (see $(LDLIBS)). - - This should be done in the $(CC) args passing style - when LD := $(CC), e.g. `-Wl,-foo', as opposed to - `-foo'. - -$(LDLIBS) : Libraries to pass to the linker (e.g. -lltp, etc). - -$(LTPLDLIBS) : LTP internal libraries i.e. these in libs/ directory. - -$(OPT_CFLAGS) : Optimization flags to pass into the C compiler, -O2, - etc. If you specify -O2 or higher, you should also - specify -fno-strict-aliasing, because of gcc - fstrict-aliasing optimization bugs in the tree - optimizer. Search for `fstrict-aliasing optimization - bug' with your favorite search engine. - - Examples of more recent bugs: - 1. tree-optimization/17510 - 2. tree-optimization/39100 - - Various bugs have occurred in the past due to buggy - logic in the tree-optimization portion of the gcc - compiler, from 3.3.x to 4.4. - -$(RANLIB) : What to run after archiving a library. - -$(WCFLAGS) : Warning flags to pass to $(CC), e.g. -Werror, - -Wall, etc. -------------------------------------------------------------------------------- - -Make System Variables ---------------------- - -A series of variables are used within the make system that direct what actions -need to be taken. Rather than me listing the variables here, please with their -intended uses, please refer to the comments contained in -+.../include/mk/env_pre.mk+. - -Guidelines and Recommendations ------------------------------- - -Of course, the GNU Make manual is key to understanding the Make system, but -here are the following sections and chapters I suggest reviewing: - -link:http://www.gnu.org/software/make/manual/make.html#Implicit-Rules[Implicit Rules] -link:http://www.gnu.org/software/make/manual/make.html#Using-Variables[Variables and Expansion] -link:http://www.gnu.org/software/make/manual/make.html#Origin-Function[Origin Use] -link:http://www.gnu.org/software/make/manual/make.html#Directory-Search[VPath Use] - -Before Committing ------------------ - -One should rebuild from scratch before committing. Please see INSTALL for more -details. - -Other Errata ------------- - -Please see TODO for any issues related to the Makefile infrastructure, and -build structure / source tree in general. diff --git a/doc/C-Test-Case-Tutorial.asciidoc b/doc/C-Test-Case-Tutorial.asciidoc deleted file mode 100644 index 07a61f4e..00000000 --- a/doc/C-Test-Case-Tutorial.asciidoc +++ /dev/null @@ -1,1079 +0,0 @@ -C Test Case Tutorial -==================== - -NOTE: See also - https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[Test Writing Guidelines], - https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API]. - -This is a step-by-step tutorial on writing a simple C LTP test, where topics -of the LTP and Linux kernel testing will be introduced gradually using a -concrete example. Most sections will include exercises, some trivial and -others not so much. If you find an exercise is leading you off at too much of -a tangent, just leave it for later and move on. - -LTP tests can be written in C or Shell script. This tutorial is only for tests -written in C using the new LTP test API. Note that while we go into some -detail on using Git, this is not intended as a canonical or complete guide -for Git. - -0. Assumptions & Feedback -------------------------- - -We assume the reader is familiar with C, Git and common Unix/Linux/GNU tools -and has some general knowledge of Operating Systems. Experienced Linux -developers may find it too verbose while people new to system level Linux -development may find it overwhelming. - -Comments and feedback are welcome, please direct them to -https://lists.linux.it/listinfo/ltp[the mailing list]. - -1. Getting Started ------------------- - -Git-clone the main LTP repository as described in -https://github.com/linux-test-project/ltp#quick-guide-to-running-the-tests[the +README.md+] -and change directory to the checked-out Git repository. We recommend installing the LTP -and running one of the tests mentioned in the Quick guide (in the +README.md+) to -ensure you are starting from a good state. - -We also recommended cloning the Linux kernel repository for reference, this -guide will refer to files and directories within the mainline kernel 4.12. - -[source,shell] ------------------------------------------------------------------------------- -$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ------------------------------------------------------------------------------- - -There are a number of other repositories which are useful for reference as -well, including the GNU C library +glibc+ and the alternative C library -+musl+. Some system calls are partially or even entirely implemented in user -land as part of the standard C library. So in these cases, the C library is an -important reference. +glibc+ is the most common C library for Linux, however -+musl+ is generally easier to understand. - -How system calls are implemented varies from one architecture to another and -across kernel and C library versions. To find out whether a system call is -actually accessing the kernel (whether it is actually a system call) on any -given machine you can use the +strace+ utility. This intercepts system calls -made by an executable and prints them. We will use this later in the tutorial. - -2. Choose a System Call to test -------------------------------- - -We will use the +statx()+ system call, to provide a concrete example of a -test. At the time of writing there is no test for this call which was -introduced in Linux kernel version 4.11. - -Linux system call specific tests are primarily contained in -+testcases/kernel/syscalls+, but you should also +git grep+ the entire LTP -repository to check for any existing usages of a system call. - -One way to find a system call which is not currently tested by the LTP is to -look at +include/linux/syscalls.h+ in the kernel tree. - -Something the LTP excels at is ensuring bug-fixes are back ported to -maintenance releases, so targeting a specific regression is another -option. - -2.1. Find an untested System call -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Try to find an untested system call which has a manual page (i.e. +man -syscall+ produces a result). It is a good idea to Git-clone the latest kernel -man-pages repository. - -[source,shell] ------------------------------------------------------------------------------- -$ git clone git://git.kernel.org/pub/scm/docs/man-pages/man-pages.git ------------------------------------------------------------------------------- - -At the time of writing the difference between the latest man-pages release and -the +HEAD+ of the repository (usually the latest commit) is well over 100 -commits. This represents about 9 weeks of changes. If you are using a stable -Linux distribution, your man-pages package may well be years old. So as with -the kernel, it is best to have the Git repository as a reference. - -You could also find a system call with untested parameters or use whatever it -is you are planning to use the LTP for. - -3. Create the test skeleton ---------------------------- - -I shall call my test +statx01.c+, by the time you read this that file name -will probably be taken, so increment the number in the file name as -appropriate or replace +statx+ with the system call chosen in exercise 2.1. - -[source,shell] ------------------------------------------------------------------------------- -$ mkdir testcases/kernel/syscalls/statx -$ cd testcases/kernel/syscalls/statx -$ echo statx >> .gitignore ------------------------------------------------------------------------------- - -Next open +statx01.c+ and add the following boilerplate. Make sure to change -the copy right notice to your name/company, correct the test name and minimum -kernel version if necessary. I will explain what the code does below. - -[source,c] ------------------------------------------------------------------------------- -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2017 Instruction Ignorer <"can't"@be.bothered.com> - */ - -/*\ - * [Description] - * - * All tests should start with a description of _what_ we are testing. - * Non-trivial explanations of _how_ the code works should also go here. - * Include relevant links, Git commit hashes and CVE numbers. - * Inline comments should be avoided. - */ - -#include "tst_test.h" - -static void run(void) -{ - tst_res(TPASS, "Doing hardly anything is easy"); -} - -static struct tst_test test = { - .test_all = run, - .min_kver = "4.11", -}; ------------------------------------------------------------------------------- - -Starting with the +#include+ statement we copy in the main LTP test library -headers. This includes the most common test API functions and the test harness -initialisation code. It is important to note that this is a completely -ordinary, independent C program, however +main()+ is missing because it is -implemented in +tst_test.h+. - -We specify what code we want to run as part of the test using the +tst_test -test+ structure. Various callbacks can be set by the test writer, including -+test.test_all+, which we have set to +run()+. The test harness will execute -this callback in a separate process (using +fork()+), forcibly terminating it -if it does not return after +test.timeout+ seconds. - -We have also set +test.min_kver+ to the kernel version where +statx+ was -introduced. The test library will determine the kernel version at runtime. If -the version is less than 4.11 then the test harness will return +TCONF+, -indicating that this test is not suitable for the current system -configuration. - -Occasionally features are back ported to older kernel versions, so +statx+ may -exist on kernels with a lower version. However we don't need to worry about -that unless there is evidence of it happening. - -As mentioned in the code itself, you should specify what you are testing and -the expected outcome, even if it is relatively simple. If your program flow is -necessarily complex and difficult to understand (which is often the case when -trying to manipulate the kernel into doing something bad), then a detailed -explanation of how the code works is welcome. - -What you should not do, is use inline comments or include the same level of -explanation which is written here. As a general rule, if something is easy to -document, then the code should also be easy to read. So don't document the easy -stuff (except for the basic test specification). - -Before continuing we should compile this and check that the basics work. In -order to compile the test we need a +Makefile+ in the same subdirectory. If -one already exists, then nothing needs to be done, otherwise add one with the -following contents. - -[source,make] ------------------------------------------------------------------------------- -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2019 Linux Test Project - -top_srcdir ?= ../../../.. - -include $(top_srcdir)/include/mk/testcases.mk - -include $(top_srcdir)/include/mk/generic_leaf_target.mk - ------------------------------------------------------------------------------- - -This will automatically add +statx01.c+ as a build target producing a -+statx01+ executable. Unless you have heavily deviated from the tutorial, and -probably need to change +top_srcdir+, nothing else needs to be done. - -Normally, if you were starting a Makefile from scratch, then you would need to -add +statx01+ as a build target. Specifying that you would like to run some -program (e.g. +gcc+ or +clang+) to transform +statx01.c+ into +statx01+. Here -we don't need to do that, but sometimes it is still necessary. For example, if -we needed to link to the POSIX threading library, then we could add the -following line after +testcases.mk+. - -[source,make] --------------------------------------------------------------------------------- -statx01: CFLAGS += -pthread --------------------------------------------------------------------------------- - -Assuming you are in the test's subdirectory +testcases/kernel/syscalls/statx+, -do - -[source,shell] --------------------------------------------------------------------------------- -$ make -$ ./statx01 --------------------------------------------------------------------------------- - -This should build the test and then run it. However, even though the test is -in the +syscalls+ directory it won't be automatically ran as part of the -_syscalls_ test group (remember +./runltp -f syscalls+ from the +README.md+?). For -this we need to add it to the +runtest+ file. So open +runtest/syscalls+ and add -the lines starting with a +++. - -[source,diff] --------------------------------------------------------------------------------- - statvfs01 statvfs01 - statvfs02 statvfs02 - -+statx01 statx01 -+ - stime01 stime01 - stime02 stime02 - --------------------------------------------------------------------------------- - -The +runtest+ files are in a two column format. The first column is the test -name, which is mainly used by test runners for reporting and filtering. It is -just a single string of text with no spaces. The second column, which can -contain spaces, is passed to the shell in order to execute the test. Often it -is just the executable name, but some tests also take arguments (the LTP has a -library for argument parsing, by the way). - -If you haven't done so already, we should add all these new files to Git. It -is vitally important that you do not make changes to the master branch. If you -do then pulling changes from upstream becomes a major issue. So first of all -create a new branch. - -[source,shell] --------------------------------------------------------------------------------- -$ git checkout -b statx01 master --------------------------------------------------------------------------------- - -Now we want to add the files we have created or modified, but before doing a -commit make sure you have configured Git correctly. You need to at least set -your Name and e-mail address in +~/.gitconfig+, but there are some other -settings which come in handy too. My relatively simple configuration is similar to -the below - -[source,conf] --------------------------------------------------------------------------------- -[user] - name = Sarah Jane - email = sjane@e-mail.address -[core] - editor = emacs -[sendemail] - smtpServer = smtp.server.address --------------------------------------------------------------------------------- - -Obviously you need to at least change your name and e-mail. The SMTP server is -useful for +git send-email+, which we will discuss later. The editor value is -used for things like writing commits (without the +-m+ option). - -[source,shell] --------------------------------------------------------------------------------- -$ git add -v :/testcases/kernel/syscalls/statx :/runtest/syscalls -$ git commit -m "statx01: Add new test for statx syscall" --------------------------------------------------------------------------------- - -This should add all the new files in the +statx+ directory and the +runtest+ -file. It is good practice to commit early and often. Later on we will do a -Git-rebase, which allows us to clean up the commit history. So don't worry -about how presentable your commit log is for now. Also don't hesitate to -create a new branch when doing the exercises or experimenting. This will allow -you to diverge from the tutorial and then easily come back again. - -I can't emphasize enough that Git makes things easy through branching and that -things quickly get complicated if you don't do it. However if you do get into -a mess, Git-reflog and Git-reset, will usually get you out of it. If you also -mess that up then it may be possible to cherry pick 'dangling' commits out of -the database into a branch. - -3.1 Report TCONF instead of TPASS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Maybe the test should report "TCONF: Not implemented" instead or perhaps -+TBROK+. Try changing it do so (see +doc/test-writing-guidelines.txt+ or -https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[the -Wiki]). - -3.2 Check Git ignores the executable -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Is your +.gitignore+ correct? - -3.3 Run make check -~~~~~~~~~~~~~~~~~~ - -Check coding style with `make check` - (more in https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#21-c-coding-style[C coding style]) - -3.4 Install the LTP and run the test with runtest -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Run +statx01+ on its own; similar to the +madvise+ tests in the +README.md+. - -4. Call the system call ------------------------ - -At the time of writing +statx+ has no +glibc+ wrapper. It is also fairly common -for a distribution's C library version to be older than its kernel or it may use a -cut down C library in comparison to the GNU one. So we must call +statx()+ -using the general +syscall()+ interface. - -The LTP contains a library for dealing with the +syscall+ interface, which is -located in +include/lapi+. System call numbers are listed against the relevant -call in the +*.in+ files (e.g. +x86_64.in+) which are used to generate -+syscalls.h+, which is the header you should include. On rare occasions you -may find the system call number is missing from the +*.in+ files and will need -to add it (see +include/lapi/syscalls/strip_syscall.awk+). - -System call numbers vary between architectures, hence there are multiple -+*.in+ files for each architecture. You can find the various values for the -+statx+ system call across a number of +unistd.h+ files in the Linux kernel. - -Note that we don't use the system-call-identifier value available in -+/usr/include/linux/uinstd.h+ because the kernel might be much newer than the -user land development packages. - -For +statx+ we had to add +statx 332+ to +include/lapi/syscalls/x86_64.in+, -+statx 383+ to +include/lapi/syscalls/powerpc.in+, etc. Now lets look at -the code, which I will explain in more detail further down. - -[source,c] --------------------------------------------------------------------------------- -/* - * Test statx - * - * Check if statx exists and what error code it returns when we give it dodgy - * data. - */ - -#include -#include "tst_test.h" -#include "lapi/syscalls.h" - -struct statx_timestamp { - int64_t tv_sec; - uint32_t tv_nsec; - int32_t __reserved; -}; - -struct statx { - uint32_t stx_mask; - uint32_t stx_blksize; - uint64_t stx_attributes; - uint32_t stx_nlink; - uint32_t stx_uid; - uint32_t stx_gid; - uint16_t stx_mode; - uint16_t __spare0[1]; - uint64_t stx_ino; - uint64_t stx_size; - uint64_t stx_blocks; - uint64_t stx_attributes_mask; - struct statx_timestamp stx_atime; - struct statx_timestamp stx_btime; - struct statx_timestamp stx_ctime; - struct statx_timestamp stx_mtime; - uint32_t stx_rdev_major; - uint32_t stx_rdev_minor; - uint32_t stx_dev_major; - uint32_t stx_dev_minor; - uint64_t __spare2[14]; -}; - -static int sys_statx(int dirfd, const char *pathname, int flags, - unsigned int mask, struct statx *statxbuf) -{ - return tst_syscall(__NR_statx, dirfd, pathname, flags, mask, statxbuf); -} - -... --------------------------------------------------------------------------------- - -So the top part of the code is now boiler plate for calling +statx+. It is -common for the kernel to be newer than the user land libraries and headers. So -for new system calls like +statx+, we copy, with a few modifications, the -relevant definitions into the LTP. This is somewhat like 'vendoring', although -we are usually just copying headers required for interacting with the Kernel's -ABI (Application Binary Interface), rather than internalising actual -functionality. - -So from the top we include the +stdint.h+ library which gives us the standard -+(u)int*_t+ type definitions. We use these in place of the Kernel type -definitions such as +__u64+ in +linux/types.h+. We then have a couple of -structure definitions which form part of the +statx+ API. These were copied -from +include/uapi/linux/stat.h+ in the Kernel tree. - -After that, there is a wrapper function, which saves us from writing -+tst_syscall(__NR_statx, ...+, every time we want to make a call to -+statx+. This also provides a stub for when +statx+ is eventually integrated -into the LTP library and also implemented by the C library. At that point we -can switch to using the C library implementation if available or fallback to -our own. - -The advantage of using the C library implementation is that it will often be -better supported across multiple architectures. It will also mean we are using -the system call in the same way most real programs would. Sometimes there are -advantages to bypassing the C library, but in general it should not be our -first choice. - -The final test should do a check during configuration (i.e. when we run -+./configure+ before building) which checks if the +statx+ system call and -associated structures exists. This requires writing an +m4+ file for use with -+configure.ac+ which is processed during +make autotools+ and produces the -configure script. - -For the time being though we shall just ignore this. All you need to know for -now is that this is a problem which eventually needs to be dealt with and that -there is a system in place to handle it. - -[source,c] --------------------------------------------------------------------------------- -... - -static void run(void) -{ - struct statx statxbuf = { 0 }; - - TEST(sys_statx(0, NULL, 0, 0, &statxbuf)); - - if (TST_RET == 0) - tst_res(TFAIL, "statx thinks it can stat NULL"); - else if (TST_ERR == EFAULT) - tst_res(TPASS, "statx set errno to EFAULT as expected"); - else - tst_res(TFAIL | TERRNO, "statx set errno to some unexpected value"); -} - -static struct tst_test test = { - .test_all = run, - .min_kver = "4.11", -}; --------------------------------------------------------------------------------- - -The +TEST+ macro sets +TST_RET+ to the return value of +tst_statx()+ and -+TST_ERR+ to the value of +errno+ immediately after the functions -return. This is mainly just for convenience, although it potentially could -have other uses. - -We check whether the return value indicates success and if it doesn't also -check the value of +errno+. The last call to +tst_res+ includes +TERRNO+, -which will print the current error number and associated description in -addition to the message we have provided. Note that it uses the current value -of +errno+ not +TST_ERR+. - -What we should have done in the example above is use +TTERRNO+ which takes the -value of +TST_ERR+. - -If we try to run the test on a kernel where +statx+ does not exist, then -+tst_syscall+ will cause it to fail gracefully with +TCONF+. Where +TCONF+ -indicates the test is not applicable to our configuration. - -The function +tst_syscall+ calls +tst_brk(TCONF,...)+ on failure. +tst_brk+ -causes the test to exit immediately, which prevents any further test code from -being run. - -4.1 What are the differences between tst_brk and tst_res? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -See +include/tst_test.h+ and the -https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[test -writing guide]. Also what do they have in common? - -4.2 What happens if you call tst_res(TINFO, ...) after sys_statx? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Does the test still function correctly? - -4.3 Extend the test to handle other basic error conditions. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For example, see if you can trigger +ENOENT+ instead. You shouldn't -have to create any files, which is discussed in the next section. - -5. Setup, Cleanup and files ---------------------------- - -Some tests require resources to be allocated, or system settings to be -changed, before the test begins. This 'setup' only has to be done once at the -beginning and at the end of the test needs to be removed or reverted. The -'cleanup' also has to be done regardless of whether the test breaks. - -Fortunately, like most test libraries, we have setup and cleanup (teardown) -callbacks. +setup+ is called once before +run+ and +cleanup+ is called once -afterwards. Note that +run+ itself can be called multiple times by the test -harness, but that +setup+ and +cleanup+ are only called once. - -If either your code, a +SAFE_*+ macro or a library function such as -+tst_syscall+ call +tst_brk+, then +run+ will exit immediately and the -+cleanup+ function is then called. Once 'cleanup' is completed, the test -executable will then exit altogether abandoning any remaining iterations of -+run+. - -For +statx+ we would like to create some files or file like objects which we -have control over. Deciding where to create the files is easy, we just create -it in the current working directory and let the LTP test harness handle where -that should be by setting +.needs_tmpdir = 1+. - -[source,c] --------------------------------------------------------------------------------- -/* - * Test statx - * - * Check if statx exists and what error code it returns when we give it dodgy - * data. Then stat a file and check it returns success. - */ - -#include -#include "tst_test.h" -#include "lapi/syscalls.h" -#include "lapi/fcntl.h" - -#define FNAME "file_to_stat" -#define STATX_BASIC_STATS 0x000007ffU - -/*************** statx structure and wrapper goes here ! ***************/ - -... --------------------------------------------------------------------------------- - -We have added an extra include +lapi/fcntl.h+ which wraps the system header by -the same name (+#include +). This header ensures we have definitions -for recently added macros such as +AT_FDCWD+ by providing fall backs if the -system header does not have them. The +lapi+ directory contains a number of -headers like this. - -At some point we may wish to add +lapi/stat.h+ to provide a fall back for -macros such as +STATX_BASIC_STATS+. However for the time being we have just -defined it in the test. - -[source,c] --------------------------------------------------------------------------------- -... - -static void setup(void) -{ - SAFE_TOUCH(FNAME, 0777, NULL); -} - -static void run(void) -{ - struct statx statxbuf = { 0 }; - - TEST(sys_statx(0, NULL, 0, 0, &statxbuf)); - if (TST_RET == 0) - tst_res(TFAIL, "statx thinks it can stat NULL"); - else if (TST_ERR == EFAULT) - tst_res(TPASS, "statx set errno to EFAULT as expected"); - else - tst_res(TFAIL | TERRNO, "statx set errno to some unexpected value"); - - TEST(sys_statx(AT_FDCWD, FNAME, 0, STATX_BASIC_STATS, &statxbuf)); - if (TST_RET == 0) - tst_res(TPASS, "It returned zero so it must have worked!"); - else - tst_res(TFAIL | TERRNO, "statx can not stat a basic file"); -} - -static struct tst_test test = { - .setup = setup, - .test_all = run, - .min_kver = "4.11", - .needs_tmpdir = 1 -}; --------------------------------------------------------------------------------- - -The +setup+ callback uses one of the LTP's +SAFE+ functions to create an empty -file +file_to_stat+. Because we have set +.needs_tmpdir+, we can just create -this file in the present working directory. We don't need to create a -+cleanup+ callback yet because the LTP test harness will recursively delete -the temporary directory and its contents. - -The +run+ function can be called multiple times by the test harness, however -+setup+ and +cleanup+ callbacks will only be ran once. - -[WARNING] -By this point you may have begun to explore the LTP library headers or older -tests. In which case you will have come across functions from the old API such -as +tst_brkm+. The old API is being phased out, so you should not use these -functions. - -So far we haven't had to do any clean up. So our example doesn't answer the -question "what happens if part of the clean up fails?". To answer this we are -going to modify the test to ask the (highly contrived) question "What happens -if I create and open a file, then create a hard-link to it, then call open -again on the hard-link, then 'stat' the file". - -[source,c] --------------------------------------------------------------------------------- -#define LNAME "file_to_stat_link" - -... - -static void setup(void) -{ - fd = SAFE_OPEN(FNAME, O_CREAT, 0777); - SAFE_LINK(FNAME, LNAME); - lfd = SAFE_OPEN(LNAME, 0); -} - -static void cleanup(void) -{ - if (lfd != 0) - SAFE_CLOSE(lfd); - - if (fd != 0) - SAFE_CLOSE(fd); -} - -static void run(void) -{ - ... - - TEST(sys_statx(AT_FDCWD, LNAME, 0, STATX_BASIC_STATS, &statxbuf)); - if (TST_RET == 0) - tst_res(TPASS, "It returned zero so it must have worked!"); - else - tst_res(TFAIL | TERRNO, "statx can not stat a basic file"); -} - -static struct tst_test test = { - .setup = setup, - .cleanup = cleanup, - .test_all = run, - .tcnt = 2, - .min_kver = "4.11", - .needs_tmpdir = 1 -}; --------------------------------------------------------------------------------- - -Because we are now opening a file, we need a +cleanup+ function to close the -file descriptors. We have to manually close the files to ensure the temporary -directory is deleted by the test harness (see the -https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[test -writing guidelines] for details). - -As a matter of good practice, the file descriptors are closed in reverse -order. In some circumstances the order in which clean up is performed is -significant. In that case resources created towards the end of 'setup' are -dependent on ones near the beginning. So during 'cleanup' we remove the -dependants before their dependencies. - -If, for some reason, the file descriptor +lfd+ became invalid during the test, -but +fd+ was still open, we do not want +SAFE_CLOSE(lfd)+ to cause the -+cleanup+ function to exit prematurely. If it did, then +fd+ would remain open -which would cause problems on some file systems. - -Nor do we want to call +cleanup+ recursively. So during 'cleanup' +tst_brk+, -and consequently the +SAFE+ functions, do not cause the test to exit with -+TBROK+. Instead they just print an error message with +TWARN+. - -It is not entirely necessary to check if the file descriptors have a none zero -value before attempting to close them. However it avoids a bunch of spurious -warning messages if we fail to open +file_to_stat+. Test case failures can be -difficult to interpret at the best of times, so avoid filling the log with -noise. - -5.1 Check statx returns the correct number of hard links -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The field +statx.stx_nlink+ should be equal to 2, right? - -5.2 Git-branch -~~~~~~~~~~~~~~ - -We are about to make some organisational changes to the test, so now would be -a good time to branch. Then we can switch between the old and new versions, to -check the behavior has not been changed by accident. - -6. Split the test ------------------ - -In our current test, we have essentially rolled two different test cases into -one. Firstly we check if an error is returned when bad arguments are provided -and secondly we check what happens when we stat an actual file. Quite often it -makes sense to call +tst_res+ multiple times in a single test case because we -are checking different properties of the same result, but here we are clearly -testing two different scenarios. - -So we should split the test in two. One obvious way to do this is to create -+statx02.c+, but that seems like overkill in order to separate two simple test -cases. So, for now at least, we are going to do it a different way. - -[source,c] --------------------------------------------------------------------------------- -... - -static void run_stat_null(void) -{ - struct statx statxbuf = { 0 }; - - TEST(sys_statx(0, NULL, 0, 0, &statxbuf)); - if (TST_RET == 0) - tst_res(TFAIL, "statx thinks it can stat NULL"); - else if (TST_ERR == EFAULT) - tst_res(TPASS, "statx set errno to EFAULT as expected"); - else - tst_res(TFAIL | TERRNO, "statx set errno to some unexpected value"); -} - -static void run_stat_symlink(void) -{ - struct statx statxbuf = { 0 }; - - TEST(sys_statx(AT_FDCWD, LNAME, 0, STATX_BASIC_STATS, &statxbuf)); - if (TST_RET == 0) - tst_res(TPASS, "It returned zero so it must have worked!"); - else - tst_res(TFAIL | TERRNO, "statx can not stat a basic file"); -} - -static void run(unsigned int i) -{ - switch(i) { - case 0: run_stat_null(); - case 1: run_stat_symlink(); - } -} - -static struct tst_test test = { - .setup = setup, - .cleanup = cleanup, - .test = run, - .tcnt = 2, - .min_kver = "4.11", - .needs_tmpdir = 1 -}; --------------------------------------------------------------------------------- - -So we have used an alternative form of the +test+ or +run+ callback which -accepts an index. Some tests use this index with an array of parameters and -expected return values. Others do something similar to the above. The index -can be used how you want so long as each iteration calls +tst_res+ in a -meaningful way. - -If an iteration fails to return a result (i.e. call +tst_res+ with a value -other than +TINFO+) then the test harness will report +TBROK+ and print the -iteration which failed. This prevents a scenario in your test from silently -failing due to some faulty logic. - -6.1 What is wrong with the switch statement? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Were you paying attention? Also see the output of +make check+. - -6.2 Test a feature unique to statx -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -So far we have not tested anything which is unique to +statx+. So, for -example, you could check stx_btime is correct (possibly only to within a -margin of error) and that it differs from +stx_mtime+ after writing to the -file. - -Alternatively you could check that +stx_dev_major+ and +stx_dev_minor+ are set -correctly. Note that the LTP has helper functions for creating devices and -file systems (see -https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#2214-testing-with-a-block-device[section -2.2.14] of the Test Writing Guidelines). - -This could be quite a challenging exercise. You may wish to tackle an -altogether different test scenario instead. If you get stuck just move onto -the next section and come back later. - -7. Submitting the test for review ---------------------------------- - -Ignoring the fact we should probably create +lapi/stat.h+ along with a bunch -of fallback logic in the build system. We can now get our test ready for -submission. - -The first thing you need to do before considering submitting your test is run -+make check-statx01+ or + make check+ in the test's directory. Again, we use -the kernel style guidelines where possible. Next you should create a new -branch, this will allow you to reshape your commit history without fear. - -After that we have the pleasure of doing an interactive 'rebase' to clean up -our commit history. In its current form the test only really needs a single -commit, but if you have been using Git correctly then you should have -many. The main reason we want to compress it to a single commit, is to make -the LTP's Git-log readable. It also allows us to write a coherent description -of the work as a whole in retrospective. Although, when adding a new test, the -test description in the code will probably make the commit message redundant. - -Anyway, as an example, we shall look at my personal commit history from this -tutorial and 'rebase' it. You should try following along with your own -repository. First lets look at the commit history since we branched from -master. - -[source,shell] --------------------------------------------------------------------------------- -$ git log --oneline master..HEAD -152d39fe7 (HEAD -> tutorial-rebase2, tutorial-rebase) tutorial: Start Submitting patch section -70f7ce7ce statx01: Stop checkpatch from complaining -bb0332bd7 tutorial: Fix review problems -6a87a084a statx01: Fix review problems -d784b1e85 test-writing-guidelines: Remove old API argument -c26e1be7a fixup! tutorial -1e24a5fb5 (me/tutorial-rebase) fixup! tutorial -568a3f7be fixup! tutorial -09dd2c829 statx: stage 6 -bfeef7902 statx: stage 5b -76e03d714 statx: stage 5a -98f5bc7ac statx: stage 4 -6f8c16438 statx: stage 3 (Add statx01) -5d93b84d8 Add statx and other syscall numbers -5ca627b78 tutorial: Add a step-by-step C test tutorial --------------------------------------------------------------------------------- - -So we have told git to show all the commits which don't exist in 'master', but -are in +HEAD+, where +HEAD+ is the top of the current branch. The current -branch is +tutorial-rebase2+ which I just created. I have already done one -'rebase' and submitted a patch for review, so my original branch was just called -+tutorial+. - -As usual my commit history is starting to look like a bit of mess! There is -even a commit in there which should not be in the this branch (Remove old API -argument), however it can be ignored for now and 'cherry picked' into a new branch -later. - -For my patch I actually need at least two commits, one which contains the -tutorial text and one which contains the test and associated files. So first -of all I want to 'squash' (amalgamate) all the commits appended with -+tutorial:+ into the bottom commit. - -[source,shell] --------------------------------------------------------------------------------- -$ git rebase -i 5ca627b78\^ -... --------------------------------------------------------------------------------- - -This begins an interactive 'rebase' where commit 5ca6427b78 is the earliest -commit we want to edit. The +^+ symbol after the commit hash, specifies the -commit before this one. The interactive 'rebase' command takes the last commit -we want to keep unaltered as it's argument (in other words it takes a -non-inclusive range). - -Upon entering a similar command you will be presented with a text file -similar to the following. The file should be displayed in your text editor of -choice, if it doesn't, then you may change the editor variable in +.gitconfig+ -which was shown in section 3. - -[source,rebase] --------------------------------------------------------------------------------- -pick 5ca627b78 tutorial: Add a step-by-step C test tutorial -pick 5d93b84d8 Add statx and other syscall numbers -pick 6f8c16438 statx: stage 3 (Add statx01) -pick 98f5bc7ac statx: stage 4 -pick 76e03d714 statx: stage 5a -pick bfeef7902 statx: stage 5b -pick 09dd2c829 statx: stage 6 -pick 568a3f7be fixup! tutorial -pick 1e24a5fb5 fixup! tutorial -pick c26e1be7a fixup! tutorial -pick d784b1e85 test-writing-guidelines: Remove old API argument -pick 6a87a084a statx01: Fix review problems -pick bb0332bd7 tutorial: Fix review problems -pick 70f7ce7ce statx01: Stop checkpatch from complaining -pick 152d39fe7 tutorial: Start Submitting patch section --------------------------------------------------------------------------------- - -The last commit from Git-log is shown at the top. The left hand column -contains the commands we want to run on each commit. +pick+ just means we -re-apply the commit as-is. We can reorder the lines to apply the commits in a -different order, but we need to be careful when reordering commits to the same -file. If your 'rebase' results in a merge conflict, then you have probably -reordered some commits which contained changes to the same piece of code. - -Perhaps a better name for the interactive 'rebase' command would be 'replay'. As -we pick a point in the commit history, undo all those commits before that -point, then reapply them one at a time. During the replay we can reorder the -commits, drop, merge, split and edit them, creating a new history. - -The commands I am going to use are +reword+ and +fixup+. The +reword+ command -allows you to edit a single commit's message. The 'fixup' command 'squashes' a -commit into the commit above/preceding it, merging the two commits into -one. The commit which has +fixup+ applied has its commit message deleted. If -you think a commit might have something useful in its message then you can use -+squash+ instead. - -[source,rebase] --------------------------------------------------------------------------------- -reword 5ca627b78 tutorial: Add a step-by-step C test tutorial -fixup 568a3f7be fixup! tutorial -fixup 1e24a5fb5 fixup! tutorial -fixup c26e1be7a fixup! tutorial -fixup bb0332bd7 tutorial: Fix review problems -fixup 152d39fe7 tutorial: Start Submitting patch section -fixup 276edecab tutorial: Save changes before rebase -pick 5d93b84d8 Add statx and other syscall numbers -pick 6f8c16438 statx: stage 3 (Add statx01) -pick 98f5bc7ac statx: stage 4 -pick 76e03d714 statx: stage 5a -pick bfeef7902 statx: stage 5b -pick 09dd2c829 statx: stage 6 -pick d784b1e85 test-writing-guidelines: Remove old API argument -pick 6a87a084a statx01: Fix review problems --------------------------------------------------------------------------------- - -So all the commits marked with +fixup+ will be re-played by Git immediately -after 5ca62 at the top. A new commit will then be created with the amalgamated -changes of all the commits and 5ca62's log message. It turns out that I didn't -need to reword anything, but there is no harm in checking. It is easy to -forget the +Signed-off-by:+ line. - -I could now do the same for the commits to the +statx+ test, making the commit -message prefixes consistent. However I am not actually going to submit the -test (yet). - -I won't attempt to show you this, but if you need to do the opposite and split -apart a commit. It is also possible using Git-rebase by marking a line with -+edit+. This will pause Git just after replaying the marked commit. You can -then use a 'soft' Git-reset to bring the selected commit's changes back into -the 'index' where you are then able to un-stage some parts before -re-committing. - -You can also use +edit+ and +git commit --amend+ together to change a commit -deep in your history, but without resetting the 'index'. The 'index' contains -changes which you have staged with +git add+, but not yet committed. - -So now that the commit history has been cleaned up, we need to submit a patch -to the mailing list or make a pull request on GitHub. The mailing list is the -preferred place to make submissions and is more difficult for most people, so -I will only cover that method. - -Just before we create the patch, we need to check that our changes will still -apply to the master branch without problems. To do this we can use another -type of 'rebase' and then try rebuilding and running the test. - -[source,shell] --------------------------------------------------------------------------------- -$ git checkout master -$ git pull origin -$ git checkout tutorial-rebase2 -$ git rebase master --------------------------------------------------------------------------------- - -Above, I update the master branch and then replay our changes onto it using -+git rebase master+. You may find that after the rebase there is a merge -conflict. This will result in something which looks like the following (taken -from a Makefile conflict which was caused by reordering commits in a 'rebase'). - -[source,diff] --------------------------------------------------------------------------------- -<<<<<<< HEAD -cve-2016-7117: LDFLAGS += -lpthread -======= -cve-2014-0196: LDFLAGS += -lpthread -lutil -lrt -cve-2016-7117: LDFLAGS += -lpthread -lrt ->>>>>>> 4dbfb8e79... Add -lrt --------------------------------------------------------------------------------- - -The first line tells us this is the beginning of a conflict. The third line -separates the two conflicting pieces of content and the last line is the end -of the conflict. Usually, all you need to do is remove the lines you don't -want, stage the changes and continue the 'rebase' with +git rebase ---continue+. - -In order to create a patch e-mail we use https://git-scm.com/docs/git-format-patch[+git format-patch+], -we can then send that e-mail using https://git-scm.com/docs/git-send-email[+git send-email+]. -It is also possible to import the patch (+mbox+) file into a number of e-mail programs. - -[source,shell] --------------------------------------------------------------------------------- -$ git format-patch -1 -v 2 -o output --to ltp@lists.linux.it fd3cc8596 -output/v2-0001-tutorial-Add-a-step-by-step-C-test-tutorial.patch --------------------------------------------------------------------------------- - -The first argument +-1+ specifies we want one commit from fd3cc8596 -onwards. If we wanted this commit and the one after it we could specify +-2+ -instead. - -This is my second patch submission so I have used +-v 2+, which indicates this -is the second version of a patch set. The +-o+ option specifies the output -directory (literally called +output+). The +--to+ option adds the +To:+ e-mail -header, which I have set to the LTP mailing list. - -We can then send this patch with the following command sans +--dry-run+. - -[source,shell] --------------------------------------------------------------------------------- -$ git send-email --dry-run output/v2-0001-tutorial-Add-a-step-by-step-C-test-tutorial.patch --------------------------------------------------------------------------------- - -Git will ask some questions (which you can ignore) and then tell you what it -would do if this weren't a dry-run. In order for this to work you have to have -a valid SMTP server set in +.gitconfig+ and also be signed up to the LTP -mailing list under the same e-mail address you have configured in Git. You can -sign up at https://lists.linux.it/listinfo/ltp. - -8. Doing code review --------------------- - -While waiting for your test to be reviewed, you are invited and encouraged to -review other contributors' code. This may seem bizarre when you are completely -new to the project, but there are two important ways in which you can -contribute here: - -A. Point out logical errors in the code. -B. Improve your own understanding - -It doesn't matter whether you know the canonical way of writing an LTP test in -C. An error of logic, when properly explained, is usually indisputable. These -are the most important errors to find as they always result in false test -results. Once someone points out such an error it is usually obvious to -everyone that it is a bug and needs to be fixed. - -Obviously testing the patch is one way of finding errors. You can apply -patches using +git am+. Then it is just a case of compiling and running the -tests. - -Finally, reading and attempting to comment on other peoples patches, gives -you a better understanding of the reviewers perspective. This is better for -the project and for you. - -Style and organisational issues are best left to after you have found logical -errors. - -9. Final notes --------------- - -Hopefully you can now grasp the structure of an LTP test and have some idea of -what is available in the LTP test library. There are a vast number of library -functions available (mainly located in include and lib), some of which are -documented in the test writing guidelines and many of which are not. - -We have only scratched the surface of the immense technical complexity of -systems programming across multiple Kernel and C lib versions as well as -different hardware architectures. The important thing to take away from this -is that you have to be conscientious of what will happen on systems different -from yours. The LTP has a huge and varied user base, so situations you may -think are unlikely can and do happen to somebody. - -Of course you don't want to spend time allowing for situations which may never -arise either, so you have to do your research and think about each situation -critically. The more systems you can test on before submitting your changes, -the better, although we understand not everyone has access to a lab. - -One important topic which has not been covered by this tutorial, is -multi-process or multi-threaded testing. The LTP library functions work inside -child processes and threads, but their semantics change slightly. There are -also various helper functions for synchronising and forking processes. For -more information see -https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API], -in particular sections -https://github.com/linux-test-project/ltp/wiki/C-Test-API#17-fork-ing[1.7 Fork()-ing] to -https://github.com/linux-test-project/ltp/wiki/C-Test-API#110-signals-and-signal-handlers[1.10 Signals and signal handlers] and -https://github.com/linux-test-project/ltp/wiki/C-Test-API#114-thread-safety-in-the-ltp-library[1.14 Thread-safety in the LTP library]. - -When it comes time to submit a test, the preferred way to do it is on the -mailing list although you can also use GitHub. The LTP follows similar rules -to the kernel for formatting and submitting patches. Generally speaking the -review cycle is easier for small patches, so try to make small changes or -additions where possible. diff --git a/doc/KVM-Test-API.asciidoc b/doc/KVM-Test-API.asciidoc deleted file mode 100644 index 7e537afc..00000000 --- a/doc/KVM-Test-API.asciidoc +++ /dev/null @@ -1,487 +0,0 @@ -LTP KVM Test API -================ - -Testing KVM is more complex than other Linux features. Some KVM bugs allow -userspace code running inside the virtual machine to bypass (emulated) hardware -access restrictions and elevate its privileges inside the guest operating -system. The worst types of KVM bugs may even allow the guest code to crash or -compromise the physical host. KVM tests therefore need to be split into two -components – a KVM controller program running on the physical host and a guest -payload program running inside the VM. The cooperation of these two components -allows testing even of bugs that somehow cross the virtualization boundary. - -NOTE: See also - https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[Test Writing Guidelines], - https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial[C Test Case Tutorial], - https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API]. - -1. Basic KVM test structure ---------------------------- - -KVM tests are simple C source files containing both the KVM controller code -and the guest payload code separated by `#ifdef COMPILE_PAYLOAD` preprocessor -condition. The file will be compiled twice. Once to compile the payload part, -once to compile the KVM controller part and embed the payload binary inside. -The result is a single self-contained binary that'll execute the embedded -payload inside a KVM virtual machine and print results in the same format as -a normal LTP test. - -A KVM test source should start with `#include "kvm_test.h"` instead of the -usual `tst_test.h`. The `kvm_test.h` header file will include the other basic -headers appropriate for the current compilation pass. Everything else in the -source file should be enclosed in `#ifdef COMPILE_PAYLOAD ... #else ... #endif` -condition, including any other header file includes. Note that the standard -LTP headers are not available in the payload compilation pass, only the KVM -guest library headers can be included. - -.Example KVM test -[source,c] -------------------------------------------------------------------------------- -#include "kvm_test.h" - -#ifdef COMPILE_PAYLOAD - -/* Guest payload code */ - -void main(void) -{ - tst_res(TPASS, "Hello, world!"); -} - -#else /* COMPILE_PAYLOAD */ - -/* KVM controller code */ - -static struct tst_test test = { - .test_all = tst_kvm_run, - .setup = tst_kvm_setup, - .cleanup = tst_kvm_cleanup, -}; - -#endif /* COMPILE_PAYLOAD */ -------------------------------------------------------------------------------- - -The KVM controller code is a normal LTP test and needs to define an instance -of `struct tst_test` with metadata and the usual setup, cleanup, and test -functions. Basic implementation of all three functions is provided by the KVM -host library. - -On the other hand, the payload is essentially a tiny kernel that'll run -on bare virtual hardware. It cannot access any files, Linux syscalls, standard -library functions, etc. except for the small subset provided by the KVM guest -library. The payload code must define a `void main(void)` function which will -be the VM entry point of the test. - -2. KVM host library -------------------- - -The KVM host library provides helper functions for creating and running -a minimal KVM virtual machine. - -2.1 Data structures -~~~~~~~~~~~~~~~~~~~ - -[source,c] -------------------------------------------------------------------------------- -struct tst_kvm_instance { - int vm_fd, vcpu_fd; - struct kvm_run *vcpu_info; - size_t vcpu_info_size; - struct kvm_userspace_memory_region ram[MAX_KVM_MEMSLOTS]; - struct tst_kvm_result *result; -}; -------------------------------------------------------------------------------- - -`struct tst_kvm_instance` holds the file descriptors and memory buffers -of a single KVM virtual machine: - -- `int vm_fd` is the main VM file descriptor created by `ioctl(KVM_CREATE_VM)` -- `int vcpu_fd` is the virtual CPU filedescriptor created by - `ioctl(KVM_CREATE_VCPU)` -- `struct kvm_run *vcpu_info` is the VCPU state structure created by - `mmap(vcpu_fd)` -- `size_t vcpu_info_size` is the size of `vcpu_info` buffer -- `struct kvm_userspace_memory_region ram[MAX_KVM_MEMSLOTS]` is the list - of memory slots defined in this VM. Unused memory slots have zero - in the `userspace_addr` field. -- `struct tst_kvm_result *result` is a buffer for passing test result data - from the VM to the controller program, mainly `tst_res()`/`tst_brk()` flags - and messages. - -[source,c] -------------------------------------------------------------------------------- -struct tst_kvm_result { - int32_t result; - int32_t lineno; - uint64_t file_addr; - char message[0]; -}; -------------------------------------------------------------------------------- - -`struct tst_kvm_result` is used to pass test results and synchronization data -between the KVM guest and the controller program. Most often, it is used -to pass `tst_res()` and `tst_brk()` messages from the VM, but special values -can also be used to send control flow requests both ways. - -- `int32_t result` is the message type, either one of the `TPASS`, `TFAIL`, - `TWARN`, `TBROK`, `TINFO` flags or a special control flow value. Errno flags - are not supported. -- `int32_t lineno` is the line number for `tst_res()`/`tst_brk()` messages. -- `uint64_t file_addr` is the VM address of the filename string for - `tst_res()`/`tst_brk()` messages. -- `char message[0]` is the buffer for arbitrary message data, most often used - to pass `tst_res()`/`tst_brk()` message strings. - -2.2 Working with virtual machines -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The KVM host library provides default implementation of the setup, cleanup -and test functions for `struct tst_test` in cases where you do not need -to customize the VM configuration. You can either assign these functions -to the `struct tst_test` instance directly or call them from your own function -that does some additional steps. All three function must be used together. - -- `void tst_kvm_setup(void)` -- `void tst_kvm_run(void)` -- `void tst_kvm_cleanup(void)` - -Note: `tst_kvm_run()` calls `tst_free_all()`. Calling it will free all -previously allocated guarded buffers. - -- `void tst_kvm_validate_result(int value)` – Validate whether the value - returned in `struct tst_kvm_result.result` can be safely passed - to `tst_res()` or `tst_brk()`. If the value is not valid, the controller - program will be terminated with error. - -- `uint64_t tst_kvm_get_phys_address(const struct tst_kvm_instance *inst, - uint64_t addr)` – Converts pointer value (virtual address) from KVM virtual - machine `inst` to the corresponding physical address. Returns 0 if - the virtual address is not mapped to any physical address. If virtual memory - mapping is not enabled in the VM or not available on the arch at all, this - function simply returns `addr` as is. - -- `int tst_kvm_find_phys_memslot(const struct tst_kvm_instance *inst, - uint64_t paddr)` – Returns index of the memory slot in KVM virtual machine - `inst` which contains the physical address `paddr`. If the address is not - backed by a memory buffer, returns -1. - -- `int tst_kvm_find_memslot(const struct tst_kvm_instance *inst, - uint64_t addr)` – Returns index of the memory slot in KVM virtual machine - `inst` which contains the virtual address `addr`. If the virtual address - is not mapped to a valid physical address backed by a memory buffer, - returns -1. - -- `void *tst_kvm_get_memptr(const struct tst_kvm_instance *inst, - uint64_t addr)` – Converts pointer value (virtual address) from KVM virtual - machine `inst` to host-side pointer. - -- `void *tst_kvm_alloc_memory(struct tst_kvm_instance *inst, unsigned int slot, - uint64_t baseaddr, size_t size, unsigned int flags)` – Allocates a guarded - buffer of given `size` in bytes and installs it into specified memory `slot` - of the KVM virtual machine `inst` at base address `baseaddr`. The buffer - will be automatically page aligned at both ends. See the kernel - documentation of `KVM_SET_USER_MEMORY_REGION` ioctl for list of valid - `flags`. Returns pointer to page-aligned beginning of the allocated buffer. - The actual requested `baseaddr` will be located at - `ret + baseaddr % pagesize`. - -- `struct kvm_cpuid2 *tst_kvm_get_cpuid(int sysfd)` – Get a list of supported - virtual CPU features returned by `ioctl(KVM_GET_SUPPORTED_CPUID)`. - The argument must be an open file descriptor returned by `open("/dev/kvm")`. - -- `void tst_kvm_create_instance(struct tst_kvm_instance *inst, - size_t ram_size)` – Creates and fully initializes a new KVM virtual machine - with at least `ram_size` bytes of memory. The VM instance info will be - stored in `inst`. - -- `int tst_kvm_run_instance(struct tst_kvm_instance *inst, int exp_errno)` – - Executes the program installed in KVM virtual machine `inst`. Any result - messages returned by the VM will be automatically printed to controller - program output. Returns zero. If `exp_errno` is non-zero, the VM execution - syscall is allowed to fail with the `exp_errno` error code and - `tst_kvm_run_instance()` will return -1 instead of terminating the test. - -- `void tst_kvm_destroy_instance(struct tst_kvm_instance *inst)` – Deletes - the KVM virtual machine `inst`. Note that the guarded buffers assigned - to the VM by `tst_kvm_create_instance()` or `tst_kvm_alloc_memory()` will - not be freed. - -The KVM host library does not provide any way to reset a VM instance back -to initial state. Running multiple iterations of the test requires destroying -the old VM instance and creating a new one. Otherwise the VM will exit -without reporting any results on the second iteration and the test will fail. -The `tst_kvm_run()` function handles this issue correctly. - -3. KVM guest library --------------------- - -The KVM guest library provides a minimal implementation of both the LTP -test library and the standard C library functions. Do not try to include -the usual LTP or C headers in guest payload code, it will not work. - -3.1 Standard C functions -~~~~~~~~~~~~~~~~~~~~~~~~ - -`#include "kvm_test.h"` - -The functions listed below are implemented according to the C standard: - -- `void *memset(void *dest, int val, size_t size)` -- `void *memzero(void *dest, size_t size)` -- `void *memcpy(void *dest, const void *src, size_t size)` -- `char *strcpy(char *dest, const char *src)` -- `char *strcat(char *dest, const char *src)` -- `size_t strlen(const char *str)` - -3.2 LTP library functions -~~~~~~~~~~~~~~~~~~~~~~~~~ - -`#include "kvm_test.h"` - -The KVM guest library currently provides the LTP functions for reporting test -results. All standard result flags except for `T*ERRNO` are supported -with the same rules as usual. However, the printf-like formatting is not -implemented yet. - -- `void tst_res(int result, const char *message)` -- `void tst_brk(int result, const char *message) __attribute__((noreturn))` - -A handful of useful macros is also available: - -- `TST_TEST_TCONF(message)` – Generates a test program that will simply print - a `TCONF` message and exit. This is useful when the real test cannot be - built due to missing dependencies or arch limitations. - -- `ARRAY_SIZE(arr)` – Returns the number of items in statically allocated - array `arr`. - -- `LTP_ALIGN(x, a)` – Aligns integer `x` to be a multiple of `a`, which - must be a power of 2. - -3.3 Arch independent functions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`#include "kvm_test.h"` - -Memory management in KVM guest library currently uses only primitive linear -buffer for memory allocation. There are no checks whether the VM can allocate -more memory and the already allocated memory cannot be freed. - -- `void *tst_heap_alloc(size_t size)` – Allocates a block of memory on the heap. - -- `void *tst_heap_alloc_aligned(size_t size, size_t align)` – Allocates - a block of memory on the heap with the starting address aligned to given - value. - -3.4 x86 specific functions -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`#include "kvm_test.h"` + -`#include "kvm_x86.h"` - -- `struct kvm_interrupt_frame` – Opaque arch-dependent structure which holds - interrupt frame information. Use the functions below to get individual values: - -- `uintptr_t kvm_get_interrupt_ip(const struct kvm_interrupt_frame *ifrm)` – - Get instruction pointer value from interrupt frame structure. This may be - the instruction which caused an interrupt or the one immediately after, - depending on the interrupt vector semantics. - -- `int (*tst_interrupt_callback)(void *userdata, - struct kvm_interrupt_frame *ifrm, unsigned long errcode)` – Interrupt handler - callback prototype. When an interrupt occurs, the assigned callback function - will be passed the `userdata` pointer that was given - to `tst_set_interrupt_callback()`, interrupt frame `ifrm` and the error - code `errcode` defined by the interrupt vector semantics. If the interrupt - vector does not generate an error code, `errcode` will be set to zero. - The callback function must return 0 if the interrupt was successfully - handled and test execution should resume. Non-zero return value means that - the interrupt could not be handled and the test will terminate with error. - -- `void tst_set_interrupt_callback(unsigned int vector, - tst_interrupt_callback func, void *userdata)` – Register new interrupt - handler callback function `func` for interrupt `vector`. The `userdata` - argument is an arbitrary pointer that will be passed to `func()` every time - it gets called. The previous interrupt handler callback will be removed. - Setting `func` to `NULL` will remove any existing interrupt handler - from `vector` and the interrupt will become fatal error. - -[source,c] -------------------------------------------------------------------------------- -struct page_table_entry_pae { - unsigned int present: 1; - unsigned int writable: 1; - unsigned int user_access: 1; - unsigned int write_through: 1; - unsigned int disable_cache: 1; - unsigned int accessed: 1; - unsigned int dirty: 1; - unsigned int page_type: 1; - unsigned int global: 1; - unsigned int padding: 3; - uint64_t address: 40; - unsigned int padding2: 7; - unsigned int prot_key: 4; - unsigned int noexec: 1; -} __attribute__((__packed__)); - -struct kvm_cpuid { - unsigned int eax, ebx, ecx, edx; -}; - -struct kvm_cregs { - unsigned long cr0, cr2, cr3, cr4; -}; - -struct kvm_sregs { - uint16_t cs, ds, es, fs, gs, ss; -}; -------------------------------------------------------------------------------- - -`struct page_table_entry_pae` is the page table entry structure for PAE and -64bit paging modes. See Intel(R) 64 and IA-32 Architectures Software -Developer's Manual, Volume 3, Chapter 4 for explanation of the fields. - -- `uintptr_t kvm_get_page_address_pae(const struct page_table_entry_pae *entry)` - – Returns the physical address of the memory page referenced by the given - page table `entry`. Depending on memory mapping changes done by the test, - the physical address may not be a valid pointer. The caller must determine - whether the address points to another page table entry or a data page, using - the known position in page table hierarchy and `entry->page_type`. Returns - zero if the `entry` does not reference any memory page. - -- `void kvm_set_segment_descriptor(struct segment_descriptor *dst, uint64_t baseaddr, uint32_t limit, unsigned int flags)` - - Fill the `dst` segment descriptor with given values. The maximum value - of `limit` is `0xfffff` (inclusive) regardless of `flags`. - -- `void kvm_parse_segment_descriptor(struct segment_descriptor *src, uint64_t *baseaddr, uint32_t *limit, unsigned int *flags)` - - Parse data in the `src` segment descriptor and copy them to variables - pointed to by the other arguments. Any parameter except the first one can - be `NULL`. - -- `int kvm_find_free_descriptor(const struct segment_descriptor *table, size_t size)` - - Find the first segment descriptor in `table` which does not have - the `SEGFLAG_PRESENT` bit set. The function handles double-size descriptors - correctly. Returns index of the first available descriptor or -1 if all - `size` descriptors are taken. - -- `unsigned int kvm_create_stack_descriptor(struct segment_descriptor *table, size_t tabsize, void *stack_base)` - - Convenience function for registering a stack segment descriptor. It'll - automatically find a free slot in `table` and fill the necessary flags. - The `stack_base` pointer must point to the bottom of the stack. - -- `void kvm_get_cpuid(unsigned int eax, unsigned int ecx, - struct kvm_cpuid *buf)` – Executes the CPUID instruction with the given - `eax` and `ecx` arguments and stores the results in `buf`. - -- `void kvm_read_cregs(struct kvm_cregs *buf)` – Copies the current values - of control registers to `buf`. - -- `void kvm_read_sregs(struct kvm_sregs *buf)` - Copies the current values - of segment registers to `buf`. - -- `uint64_t kvm_rdmsr(unsigned int msr)` – Returns the current value - of model-specific register `msr`. - -- `void kvm_wrmsr(unsigned int msr, uint64_t value)` – Stores `value` - into model-specific register `msr`. - -- `void kvm_exit(void) __attribute__((noreturn))` – Terminate the test. - Similar to calling `exit(0)` in a regular LTP test, although `kvm_exit()` - will terminate only one iteration of the test, not the whole host process. - -See Intel(R) 64 and IA-32 Architectures Software Developer's Manual -for documentation of standard and model-specific x86 registers. - -3.5 AMD SVM helper functions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`#include "kvm_test.h"` + -`#include "kvm_x86.h"` + -`#include "kvm_x86_svm.h"` - -The KVM guest library provides basic helper functions for creating and running -nested virtual machines using the AMD SVM technology. - -.Example code to execute nested VM -[source,c] -------------------------------------------------------------------------------- -int guest_main(void) -{ - ... - return 0; -} - -void main(void) -{ - struct kvm_svm_vcpu *vm; - - kvm_init_svm(); - vm = kvm_create_svm_vcpu(guest_main, 1); - kvm_svm_vmrun(vm); -} -------------------------------------------------------------------------------- - -- `int kvm_is_svm_supported(void)` - Returns non-zero value if the CPU - supports AMD SVM, otherwise returns 0. - -- `int kvm_get_svm_state(void)` - Returns non-zero value if SVM is currently - enabled, otherwise returns 0. - -- `void kvm_set_svm_state(int enabled)` - Enable or disable SVM according - to argument. If SVM is disabled by host or not supported, the test will exit - with `TCONF`. - -- `void kvm_init_svm(void)` - Enable and fully initialize SVM, including - allocating and setting up host save area VMCB. If SVM is disabled by host or - not supported, the test will exit with `TCONF`. - -- `struct kvm_vmcb *kvm_alloc_vmcb(void)` - Allocate new VMCB structure - with correct memory alignment and fill it with zeroes. - -- `void kvm_vmcb_set_intercept(struct kvm_vmcb *vmcb, unsigned int id, unsigned int state)` - - Set SVM intercept bit `id` to given `state`. - -- `void kvm_init_guest_vmcb(struct kvm_vmcb *vmcb, uint32_t asid, uint16_t ss, void *rsp, int (*guest_main)(void))` - - Initialize new SVM virtual machine. The `asid` parameter is the nested - page table ID. The `ss` and `rsp` parameters set the stack segment and stack - pointer values, respectively. The `guest_main` parameter sets the code entry - point of the virtual machine. All control registers, segment registers - (except stack segment register), GDTR and IDTR will be copied - from the current CPU state. - -- `struct kvm_svm_vcpu *kvm_create_svm_vcpu(int (*guest_main)(void), int alloc_stack)` - - Convenience function for allocating and initializing new SVM virtual CPU. - The `guest_main` parameter is passed to `kvm_init_guest_vmcb()`, - the `alloc_stack` parameter controls whether a new 8KB stack will be - allocated and registered in GDT. Interception will be enabled for `VMSAVE` - and `HLT` instructions. If you set `alloc_stack` to zero, you must configure - the stack segment register and stack pointer manually. - -- `void kvm_svm_vmrun(struct kvm_svm_vcpu *cpu)` - Start or continue execution - of a nested virtual machine. Beware that FPU state is not saved. Do not use - floating point types or values in nested guest code. Also do not use - `tst_res()` or `tst_brk()` functions in nested guest code. - -See AMD64 Architecture Programmer's Manual Volume 2 for documentation -of the Secure Virtual Machine (SVM) technology. - -4. KVM guest environment ------------------------- - -KVM guest payload execution begins with bootstrap code which will perform -the minimal guest environment setup required for running C code: - -- Activate the appropriate CPU execution mode (IA-32 protected mode - on 32-bit x86 or the 64-bit mode on x86_64). -- Create indentity mapping (virtual address = physical address) of the lower - 2GB memory region, even if parts of the region are not backed by any host - memory buffers. The memory region above 2GB threshold is left unmapped - except for one memory page reserved for the `struct tst_kvm_result` buffer. -- Initialize 8KB stack. -- Install default interrupt handlers for standard CPU exception vectors. - -When the environment setup is complete, bootstrap will call `void main(void)` -function implemented by the test program. To finish execution of guest payload, -the test can either return from the `main()` function or call `kvm_exit()` -at any point. diff --git a/doc/LTP-Library-API-Writing-Guidelines.asciidoc b/doc/LTP-Library-API-Writing-Guidelines.asciidoc deleted file mode 100644 index 3b8c1d97..00000000 --- a/doc/LTP-Library-API-Writing-Guidelines.asciidoc +++ /dev/null @@ -1,84 +0,0 @@ -LTP Library API Writing Guidelines -================================== - -NOTE: See also - https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[Test Writing Guidelines], - https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API], - https://github.com/linux-test-project/ltp/wiki/Shell-Test-API[Shell Test API]. - -1. General Rules ----------------- - -For extending library API it applies the same general rules as for writing tests, -(see https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[Test Writing Guidelines], -offline: 'doc/test-writing-guidelines.txt'), -with strong focus on readability and simplicity. - -Library tests are in 'lib/newlib_tests' directory. - -Don't forget to update docs when you change the API. - -Environment variables should be listed in -https://github.com/linux-test-project/ltp/wiki/User-Guidelines[LTP User Guidelines] -and in help output (`-h`) for both C and shell API. - -2. C API --------- - -2.1 LTP-001: Sources have tst_ prefix -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -API source code is in headers in 'include/{empty}*.h', 'include/lapi/{empty}*.h' -(backward compatibility for old kernel and libc) and C sources in 'lib/{empty}*.c'. -Files have `tst_` prefix. - -2.2 LTP-002: TST_RET and TST_ERR are not modified -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The test author is guaranteed that the test API will not modify these -variables. This prevents silent errors where the return value and -errno are overwritten before the test has chance to check them. - -The macros which are clearly intended to update these variables. That -is `TEST` and those in 'tst_test_macros.h'. Are of course allowed to -update these variables. - -2.3 LTP-003: Externally visible library symbols have the tst_ prefix -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Functions, types and variables in the public test API should have the -tst_ prefix. With some exceptions for symbols already prefixed with -`safe_` or `ltp_`. - -Static (private) symbols should not have the prefix. - - -3. Shell API ------------- - -API source code is in 'tst_test.sh', 'tst_security.sh' and 'tst_net.sh' -(all in 'testcases/lib' directory). - -Changes in the shell API should not introduce uncommon dependencies -(use basic commands installed everywhere by default). - -3.1 Shell libraries -~~~~~~~~~~~~~~~~~~~ - -Aside from shell API libraries in 'testcases/lib', it's worth putting -common code for a group of tests into a shell library. The filename -should end with '_lib.sh' and the library should load 'tst_test.sh' or -'tst_net.sh'. - -Shell libraries should have conditional expansion for 'TST_SETUP' or 'TST_CLEANUP', -to avoid surprises when test specific setup/cleanup function is redefined by -shell library. - -[source,sh] -------------------------------------------------------------------------------- -# ipsec_lib.sh -# SPDX-License-Identifier: GPL-2.0-or-later -TST_SETUP="${TST_SETUP:-ipsec_lib_setup}" -... -. tst_test.sh -------------------------------------------------------------------------------- diff --git a/doc/LTP-Release-Procedure.asciidoc b/doc/LTP-Release-Procedure.asciidoc deleted file mode 100644 index 69c05071..00000000 --- a/doc/LTP-Release-Procedure.asciidoc +++ /dev/null @@ -1,170 +0,0 @@ -LTP Release Procedure -===================== - -1. Release preparations ------------------------ - -The release procedure generally takes a few weeks. In the first week or two, -patches that should go into the release are reviewed and possibly merged. These -patches are either fixes or patches pointed out by the community. - -Patch review, when finished, is followed by a git freeze, which is a period -where only fixes are pushed to the git. During that period community is -expected to run a LTP pre-release tests, reports problems, and/or send fixes to -the mailing list. In this period we are especially making sure that there are -no regressions in the test results on a wide range of distributions and -architectures. - -Once the stabilization period has ended the time has finally come to proceed -with the release. - -2. Prepare the release notes ----------------------------- - -Part of the preparation is also to write the release notes, which are then -added to the GitHub release and also sent as announcement to various mailing -lists (see below). - -Have a look at https://lore.kernel.org/ltp/ZGNiQ1sMGvPU_ETp@yuki/ to get the -idea how it should look. - -3. Tag the git and push changes to github ------------------------------------------ - -[source,sh] --------------------------------------------------------------------- -cd ltp -echo YYYYMMDD > VERSION -git commit -S -s -m 'LTP YYYYMMDD' VERSION -git tag -s -a YYYYMMDD -m 'LTP YYYYMMDD' -git push origin master:master -git push origin YYYYMMDD --------------------------------------------------------------------- - -NOTE: The string YYYYMMDD should be substituted to the current date. - -NOTE: You can use './tools/tag-release.sh' script to have the above automated. - It allows you to verify the tag before pushing it and does other checks. - -[source,sh] --------------------------------------------------------------------- -$ ./tools/tag-release.sh -===== git push ===== -new tag: 'YYYYMMDD', previous tag: '20230127' -tag YYYYMMDD -Tagger: Person-who-released LTP -Date: ... - -LTP YYYYMMDD ------BEGIN PGP SIGNATURE----- -... ------END PGP SIGNATURE----- - -commit 3ebc2dfa85c2445bb68d8c0d66e33c4da1e1b3a7 -gpg: using RSA key ... -... -Primary key fingerprint: ... -Author: Person-who-released LTP -Date: ... - - LTP YYYYMMDD - - Signed-off-by: Person-who-released LTP - -diff --git a/VERSION b/VERSION -index af4c41fec..ae488c0e7 100644 ---- a/VERSION -+++ b/VERSION -@@ -1 +1 @@ --20230127 -+YYYYMMDD - -Please check tag and signature. Proceed? [N/y]: y -Pushing changes to upstream git. Proceed? [N/y]: y -... -To github.com:linux-test-project/ltp.git - * [new tag] YYYYMMDD -> YYYYMMDD --------------------------------------------------------------------- - -4. Prepare tarballs and metadata documentation ----------------------------------------------- - -[source,sh] --------------------------------------------------------------------- -# clone already clonned git repository to new folder -cd .. -git clone ltp ltp-full-YYYYMMDD -cd ltp-full-YYYYMMDD - -# update all submodules -git submodule update --init - -# Generate configure script -make autotools - -# Generate tarballs -cd .. -tar -cjf ltp-full-YYYYMMDD.tar.bz2 ltp-full-YYYYMMDD --exclude .git -tar -cJf ltp-full-YYYYMMDD.tar.xz ltp-full-YYYYMMDD --exclude .git - -# Generate checksums -md5 ltp-full-YYYYMMDD.tar.xz > ltp-full-YYYYMMDD.tar.xz.md5 -sha1 ltp-full-YYYYMMDD.tar.xz > ltp-full-YYYYMMDD.tar.xz.sha1 -sha256sum ltp-full-YYYYMMDD.tar.xz > ltp-full-YYYYMMDD.tar.xz.sha256 - -# Generate metadata documentation -./configure --with-metadata-generator=asciidoctor -make -C metadata -cp -v docparse/metadata.html ../metadata.YYYYMMDD.html --------------------------------------------------------------------- - -NOTE: You can use './tools/create-tarballs-metadata.sh' script to have the - above automated. All generated files are placed in ltp-release-YYYYMMDD - directory. - -[source,sh] --------------------------------------------------------------------- -$ ./tools/create-tarballs-metadata.sh -===== git clone ===== -Cloning into 'ltp-full-YYYYMMDD'... -done. -===== Update submodules ===== -Submodule 'tools/kirk' (https://github.com/linux-test-project/kirk.git) registered for path 'tools/kirk' -... -===== Generate configure script ===== -sed -n '1{s:LTP-:m4_define([LTP_VERSION],[:;s:$:]):;p;q}' VERSION > m4/ltp-version.m4 -aclocal -I m4 -... -===== Generate tarballs ===== -===== Generate checksums ===== -===== Generate metadata documentation ===== -checking for a BSD-compatible install... /usr/bin/install -c -... -'docparse/metadata.html' -> '/home/foo/ltp-release-YYYYMMDD/metadata.YYYYMMDD.html' -Generated files are in '/home/foo/ltp-release-YYYYMMDD', upload them to github --------------------------------------------------------------------- - -5. Upload the generated files to GitHub ---------------------------------------- - -Click on https://github.com/linux-test-project/ltp/releases['Releases'] then -switch to https://github.com/linux-test-project/ltp/tags['Tags'], then click on -'Add release notes'. There should be 'Attach binaries ...' link at the -bottom of the page. - -Don't forget to upload checksums for the tarballs and metadata documentation as well. - -5. Send release announcement ----------------------------- - -The announcement is sent to: - -* ltp at lists.linux.it -* linux-kernel at vger.kernel.org -* libc-alpha at sourceware.org - -CCed to: - -* lwn at lwn.net -* akpm at linux-foundation.org -* torvalds at linux-foundation.org diff --git a/doc/Maintainer-Patch-Review-Checklist.asciidoc b/doc/Maintainer-Patch-Review-Checklist.asciidoc deleted file mode 100644 index cb9e00e8..00000000 --- a/doc/Maintainer-Patch-Review-Checklist.asciidoc +++ /dev/null @@ -1,141 +0,0 @@ -# Patch Review - -Anyone can and should review patches. It's the only way to get good at -patch review and for the project to scale. - -## Goals of patch review - -1. Prevent false positive test results -2. Prevent false negative test results -3. Keep the code as simple as possible, but no simpler - -## How to find clear errors - -A clear error is one where there is unlikely to be any argument if you -provide evidence of it. Evidence being an error trace or logical proof -that an error will occur in a common situation. - -The following are examples and may not be appropriate for all tests. - -* Merge the patch locally. It should apply cleanly to master. -* Compile the patch with default and non-default configurations. - - Use sanitizers e.g. undefined behaviour, address. - - Compile on non-x86 - - Compile on x86 with -m32 -* Use `make check` -* Run effected tests in a VM - - Use single vCPU - - Use many vCPUs and enable NUMA - - Restrict RAM to < 1GB. -* Run effected tests on an embedded device -* Run effected tests on non-x86 machine in general -* Run reproducers on a kernel where the bug is present -* Run tests with "-i0" -* Compare usage of system calls with man page descriptions -* Compare usage of system calls with kernel code -* Search the LTP library for existing helper functions - -## How to find subtle errors - -A subtle error is one where you can expect some argument because you -do not have clear evidence of an error. It is best to state these as -questions and not make assertions if possible. - -Although if it is a matter of style or "taste" then senior maintainers -can assert what is correct to avoid bike shedding. - -* Ask what happens if there is an error, could it be debugged just - with the test output? -* Are we testing undefined behavior? - - Could future kernel behaviour change without "breaking userland"? - - Does the kernel behave differently depending on hardware? - - Does it behave differently depending on kernel configuration? - - Does it behave differently depending on the compiler? - - Would it behave differently if the order of checks on syscall parameters - changed in the kernel? -* Will it scale to tiny and huge systems? - - What happens if there are 100+ CPUs? - - What happens if each CPU core is very slow? - - What happens if there are 2TB of RAM? -* Are we repeating a pattern that can be turned into a library function? -* Is a single test trying to do too much? -* Could multiple similar tests be merged? -* Race conditions - - What happens if a process gets preempted? - - Could checkpoints or fuzzsync by used instead? - - Note, usually you can insert a sleep to prove a race condition - exists however finding them is hard -* Is there a simpler way to achieve the same kernel coverage? - -## How to get patches merged - -Once you think a patch is good enough you should add your Reviewed-by -and/or Tested-by tags. This means you will get some credit for getting -the patch merged. Also some blame if there are problems. - -If you ran the test you can add the Tested-by tag. If you read the -code or used static analysis tools on it, you can add the Reviewed-by -tag. - -In addition you can expect others to review your patches and add their -tags. This will speed up the process of getting your patches merged. - -## Maintainers Checklist - -Patchset should be tested locally and ideally also in maintainer's fork in -GitHub Actions on GitHub. - -NOTE: GitHub Actions do only build testing, passing the CI means only that - the test compiles fine on variety of different distributions and releases. - -The test should be executed at least once locally and should PASS as well. - -Commit messages should have - -* Author's `Signed-off-by` tag -* Committer's `Reviewed-by` or `Signed-off-by` tag -* Check also mailing lists for other reviewers / testers tags, notes and failure reports -* `Fixes: hash` if it fixes particular LTP commit -* `Fixes: #N` if it fixes github issue number N, so it's automatically closed - -After patch is accepted or rejected, set correct state and archive in -https://patchwork.ozlabs.org/project/ltp/list/[LTP patchwork instance]. - -Also update `.github/workflows/wiki-mirror.yml` script which mirrors -`doc/*.txt` to LTP wiki (git URL https://github.com/linux-test-project/ltp.wiki.git) -if new wiki page is added. - -## New tests -New test should - -* Have a record in runtest file -* Test should work fine with more than one iteration - (e.g. run with `-i 100`) -* Run with `-i 0` to check that setup and cleanup are coded properly (no test is being run) -* Have a brief description -* License: the default license for new tests is GPL v2 or later, use - GPL-2.0-or-later; the licence for test (e.g. GPL-2.0) should not change - unless test is completely rewritten -* Old copyrights should be kept unless test is completely rewritten - -### C tests -* Use new https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#22-writing-a-test-in-c[C API] -* Test binaries are added into corresponding `.gitignore` files -* Check coding style with `make check` - (more in https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#21-c-coding-style[C coding style]) -* Docparse documentation -* If a test is a regression test it should include tags - (more in https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#2238-test-tags[Test tags]) -* When rewriting old tests, https://en.wikipedia.org/wiki/%CE%9CClinux[uClinux] - support should be removed (project has been discontinued). - E.g. remove `#ifdef UCLINUX`, replace `FORK_OR_VFORK()` with simple `fork()` or `SAFE_FORK()`. - -### Shell tests -* Use new https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#23-writing-a-testcase-in-shell[shell API] -* Check coding style with `make check` - (more in https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#132-shell-coding-style[Shell coding style]) -* If a test is a regression test it should include related kernel or glibc commits as a comment - -## LTP library -For patchset touching library please check also -https://github.com/linux-test-project/ltp/wiki/LTP-Library-API-Writing-Guidelines[LTP Library API Writing Guidelines]. diff --git a/doc/Makefile b/doc/Makefile index f7e4dd02..3123b1cd 100755 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,11 +1,37 @@ +# Copyright (c) Linux Test Project, 2024-2025 # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2009, Cisco Systems Inc. -# Ngie Cooper, July 2009 -top_srcdir ?= .. +top_srcdir ?= .. include $(top_srcdir)/include/mk/env_pre.mk -RECURSIVE_TARGETS := install +PYTHON := python3 +VENV_DIR := .venv -include $(top_srcdir)/include/mk/generic_trunk_target.mk +# only fish and bash/zsh supported +VENV_CMD := if [ "x${FISH_VERSION}" != "x" ]; then . $(VENV_DIR)/bin/activate.fish; else . $(VENV_DIR)/bin/activate; fi + +RUN_VENV := if [ -d $(VENV_DIR) ]; then $(VENV_CMD); fi + +$(VENV_DIR): + $(PYTHON) -m virtualenv $(VENV_DIR) + $(VENV_CMD) && pip install -r requirements.txt + +.PHONY: setup +setup: $(VENV_DIR) + +${abs_top_builddir}/metadata/ltp.json: + $(MAKE) -C ${abs_top_builddir}/metadata + +all: ${abs_top_builddir}/metadata/ltp.json + $(RUN_VENV); sphinx-build -v -b html . html + +spelling: + $(RUN_VENV); sphinx-build -b spelling -d build/doctree . build/spelling + +clean: + rm -rf html/ build/ _static/syscalls.rst _static/tests.rst syscalls.tbl \ + ${abs_top_builddir}/metadata/ltp.json + +distclean: clean + rm -rf $(VENV_DIR) diff --git a/doc/Supported-kernel,-libc,-toolchain-versions.asciidoc b/doc/Supported-kernel,-libc,-toolchain-versions.asciidoc deleted file mode 100644 index e3d9cd92..00000000 --- a/doc/Supported-kernel,-libc,-toolchain-versions.asciidoc +++ /dev/null @@ -1,77 +0,0 @@ -Supported kernel, libc, toolchain versions -========================================== - -1. Build testing with GitHub Actions ------------------------------------- - -We test master branch in https://github.com/linux-test-project/ltp/actions[GitHub Actions] -to ensure LTP builds on various distributions including old, current and bleeding edge. -We test both gcc and clang toolchains, various architectures with cross-compilation. -For list of tested distros see -https://github.com/linux-test-project/ltp/blob/master/.github/workflows/ci.yml[.github/workflows/ci.yml]. - - -NOTE: GitHub Actions does only build testing, passing the CI means only that - the test compiles fine on variety of different distributions and releases. - GitHub Actions also uses the latest distribution image of a particular release. - -1.1 Oldest tested distributions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[align="center",options="header"] -|============================================================== -| Distro | kernel | glibc | gcc | clang -| CentOS 7 | 3.10 | 2.17 | 4.8.5 | - -| Ubuntu 18.04 LTS bionic | 4.15 | 2.27 | 7.3.0 | - -| Debian 10 oldstable (buster) | 4.19.37 | 2.28 | 8.3.0 | 7.0 -|============================================================== - -Older distributions are not officially supported, which means that it -may or may not work. It all depends on your luck. It should be possible -to compile latest LTP even on slightly older distributions than we -support with a few manual tweaks, e.g. disabling manually tests for -newly added syscalls, etc. Trivial fixes/workarounds may be accepted, -but users are encouraged to move to a newer distro. - -If latest LTP cannot be compiled even with some amount of workarounds, -you may result to older LTP releases, however these are _not_ supported -in any way. Also if you are trying to run LTP on more than 10 years old -distribution you may as well reconsider you life choices. - -1.2 Tested architectures -~~~~~~~~~~~~~~~~~~~~~~~~ - -[align="center",options="header"] -|================================== -| arch | build -| x86_64 | native -| x86 emulation | native -| aarch64 | cross compilation -| ppc64le | cross compilation -| s390x | cross compilation -|================================== - -1.3 Minimal supported kernel version -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Minimal supported kernel version is 3.10. - -1.4 Supported libc -~~~~~~~~~~~~~~~~~~ - -[align="center",options="header"] -|================================== -| Libc | Note -| https://www.gnu.org/software/libc/[GNU C Library (glibc)] | Targeted libc, tested both compilation and actual test results. -| https://uclibc-ng.org/[uClibc-ng] | Although not being tested it should work as well as it attempt to maintain a glibc compatible interface. -| https://www.uclibc.org/[uClibc] | Older https://www.uclibc.org/[uClibc] might have problems. -| https://musl.libc.org/[musl] | Not yet fully supported (see - https://github.com/linux-test-project/ltp/blob/master/ci/alpine.sh[CI script] - for list of files which need to be deleted in order to compile under musl). -| binder (Android) | Please use https://android.googlesource.com/platform/external/ltp/[AOSP fork]. -|================================== - -1.5 Used C standard -~~~~~~~~~~~~~~~~~~~ - -LTP compiles with '-std=gnu99'. diff --git a/doc/Test-Writing-Guidelines.asciidoc b/doc/Test-Writing-Guidelines.asciidoc deleted file mode 100644 index 0db852ae..00000000 --- a/doc/Test-Writing-Guidelines.asciidoc +++ /dev/null @@ -1,412 +0,0 @@ -LTP Test Writing Guidelines -=========================== - -This document describes LTP guidelines and is intended for anybody who want to -write or modify a LTP testcase. It's not a definitive guide and it's not, by -any means, a substitute for common sense. - -NOTE: See also - https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API], - https://github.com/linux-test-project/ltp/wiki/Shell-Test-API[Shell Test API], - https://github.com/linux-test-project/ltp/wiki/LTP-Library-API-Writing-Guidelines[LTP Library API Writing Guidelines]. - -Rules and recommendations which are "machine checkable" should be -tagged with an ID like +LTP-XXX+. There will be a corresponding entry -in -https://github.com/linux-test-project/ltp/tree/master/doc/rules.tsv[doc/rules.tsv]. When -you run 'make check' or 'make check-test' it will display these IDs as -a reference. - -1. Guide to clean and understandable code -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For testcases it's required that the source code is as easy to follow as -possible. When a test starts to fail the failure has to be analyzed, clean -test codebase makes this task much easier and quicker. - -Here are some hints on how to write clean and understandable code, a few of -these points are further discussed below: - -* First of all *Keep things simple* - -* Keep function and variable names short but descriptive - -* Keep functions reasonably short and focused on a single task - -* Do not overcomment - -* Be consistent - -* Avoid deep nesting - -* DRY - -1.1 Keep things simple -~~~~~~~~~~~~~~~~~~~~~~ - -For all it's worth keep the testcases simple or better as simple as possible. - -The kernel and libc are tricky beasts and the complexity imposed by their -interfaces is quite high. Concentrate on the interface you want to test and -follow the UNIX philosophy. - -It's a good idea to make the test as self-contained as possible too, ideally -tests should not depend on tools or libraries that are not widely available. - -Do not reinvent the wheel! - -* Use LTP standard interface - -* Do not add custom PASS/FAIL reporting functions - -* Do not write Makefiles from scratch, use LTP build system instead - -* Etc. - -1.2 Keep functions and variable names short -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Choosing a good name for an API functions or even variables is a difficult -task do not underestimate it. - -There are a couple of customary names for different things that help people to -understand code, for example: - -* For loop variables are usually named with a single letter 'i', 'j', ... - -* File descriptors 'fd' or 'fd_foo'. - -* Number of bytes stored in file are usually named as 'size' or 'len' - -* Etc. - -1.3 Do not overcomment -~~~~~~~~~~~~~~~~~~~~~~ - -Comments can sometimes save you day but they can easily do more harm than -good. There has been several cases where comments and actual implementation -drifted slowly apart which yielded into API misuses and hard to find bugs. -Remember there is only one thing worse than no documentation, wrong -documentation. - -Ideally everybody should write code that is obvious, which unfortunately isn't -always possible. If there is a code that requires to be commented keep it -short and to the point. These comments should explain *why* and not *how* -things are done. - -Never ever comment the obvious. - -In case of LTP testcases it's customary to add an asciidoc formatted comment -paragraph with highlevel test description at the beginning of the file right -under the GPL SPDX header. This helps other people to understand the overall -goal of the test before they dive into the technical details. It's also -exported into generated documentation hence it should mostly explain what is -tested. - -1.4 DRY (Code duplication) -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Copy & paste is a good servant but very poor master. If you are about to copy a -large part of the code from one testcase to another, think what would happen if -you find bug in the code that has been copied all around the tree. What about -moving it to a library instead? - -The same goes for short but complicated parts, whenever you are about to copy & -paste a syscall wrapper that packs arguments accordingly to machine -architecture or similarly complicated code, put it into a header instead. - -2 Coding style -~~~~~~~~~~~~~~ - -2.1 C coding style -^^^^^^^^^^^^^^^^^^ - -LTP adopted Linux kernel coding style: -https://www.kernel.org/doc/html/latest/process/coding-style.html - -If you aren't familiar with its rules please read it, it's a well written -introduction. - -Run `make check` in the test's directory and/or use `make check-$TCID`, -it uses (among other checks) our vendored version of -https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/scripts/checkpatch.pl[checkpatch.pl] -script from kernel git tree. - -NOTE: If `make check` does not report any problems, the code still may be wrong - as all tools used for checking only look for common mistakes. - -2.1.1 LTP-004: Test executable symbols are marked static -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Test executables should not export symbols unnecessarily. This means -that all top-level variables and functions should be marked with the -static keyword. The only visible symbols should be those included from -shared object files. - -2.2 Shell coding style -^^^^^^^^^^^^^^^^^^^^^^ - -When writing testcases in shell write in *portable shell* only, it's a good -idea to try to run the test using alternative shell (alternative to bash, for -example dash) too. - -*Portable shell* means Shell Command Language as defined by POSIX with an -exception of few widely used extensions, namely 'local' keyword used inside of -functions and '-o' and '-a' test parameters (that are marked as obsolete in -POSIX). - -You can either try to run the testcases on Debian which has '/bin/sh' pointing -to 'dash' by default or install 'dash' on your favorite distribution and use -it to run the tests. If your distribution lacks 'dash' package you can always -compile it from http://gondor.apana.org.au/~herbert/dash/files/[source]. - -Run `make check` in the test's directory and/or use `make check-$TCID.sh`, -it uses (among other checks) our vendored version of -https://salsa.debian.org/debian/devscripts/raw/master/scripts/checkbashisms.pl[checkbashism.pl] -from Debian, that is used to check for non-portable shell code. - -NOTE: If `make check` does not report any problems, the code still may be wrong - as `checkbashisms.pl` used for checking only looks for common mistakes. - -Here are some common sense style rules for shell - -* Keep lines under 80 chars - -* Use tabs for indentation - -* Keep things simple, avoid unnecessary subshells - -* Don't do confusing things (i.e. don't name your functions like common shell - commands, etc.) - -* Quote variables - -* Be consistent - -3 Backwards compatibility -~~~~~~~~~~~~~~~~~~~~~~~~~ - -LTP test should be as backward compatible as possible. Think of an enterprise -distributions with long term support (more than five years since the initial -release) or of an embedded platform that needs to use several years old -toolchain supplied by the manufacturer. - -Therefore LTP test for more current features should be able to cope with older -systems. It should at least compile fine and if it's not appropriate for the -configuration it should return 'TCONF'. - -There are several types of checks we use: - -The *configure script* is usually used to detect availability of a function -declarations in system headers. It's used to disable tests at compile time or -to enable fallback definitions. - -Checking the *errno* value is another type of runtime check. Most of the -syscalls returns either 'EINVAL' or 'ENOSYS' when syscall was not implemented -or was disabled upon kernel compilation. - -LTP has kernel version detection that can be used to disable tests at runtime, -unfortunately kernel version does not always corresponds to a well defined -feature set as distributions tend to backport hundreds of patches while the -kernel version stays the same. Use with caution. - -Lately we added kernel '.config' parser, a test can define a boolean -expression of kernel config variables that has to be satisfied in order for a -test to run. This is mostly used for kernel namespaces at the moment. - -Sometimes it also makes sense to define a few macros instead of creating -configure test. One example is Linux specific POSIX clock ids in -'include/lapi/posix_clocks.h'. - -3.1 Dealing with messed up legacy code -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -LTP still contains a lot of old and messy code and we are cleaning it up as -fast as we can but despite the decade of efforts there is still a lot. If you -start modifying old or a messy testcase and your changes are more complicated -than simple typo fixes you should convert the test into a new library first. - -It's also much easier to review the changes if you split them into a smaller -logical groups. The same goes for moving files. If you need a rename or move -file do it in a separate patch. - -4 License -~~~~~~~~~ - -Code contributed to LTP should be licensed under GPLv2+ (GNU GPL version 2 or -any later version). - -Use `SPDX-License-Identifier: GPL-2.0-or-later` - -5 LTP Structure -~~~~~~~~~~~~~~~ - -The structure of LTP is quite simple. Each test is a binary written either in -portable shell or C. The test gets a configuration via environment variables -and/or command line parameters, it prints additional information into the -stdout and reports overall success/failure via the exit value. - -Tests are generally placed under the 'testcases/' directory. Everything that -is a syscall or (slightly confusingly) libc syscall wrapper goes under -'testcases/kernel/syscalls/'. - -Then there is 'testcases/open_posix_testsuite/' which is a well maintained fork -of the upstream project that has been dead since 2005 and also a number of -directories with tests for more specific features. - -5.1 Runtest Files -^^^^^^^^^^^^^^^^^ - -The list of tests to be executed is stored in runtest files under the -'runtest/' directory. The default set of runtest files to be executed is -stored in 'scenario_groups/default'. When you add a test you should add -corresponding entries into some runtest file(s) as well. - -For syscall tests (these placed under 'testcases/kernel/syscalls/') use -'runtest/syscalls' file, for kernel related tests for memory management we -have 'runtest/mm', etc. - -IMPORTANT: The runtest files should have one entry per a test. Creating a - wrapper that runs all your tests and adding it as a single test - into runtest file is strongly discouraged. - -5.2 Datafiles -^^^^^^^^^^^^^ - -If your test needs datafiles to work, these should be put into a subdirectory -named 'datafiles' and installed into the 'testcases/data/$TCID' directory (to -do that you have to add 'INSTALL_DIR := testcases/data/TCID' into the -'datafiles/Makefile'). - -You can obtain path to datafiles via $TST_DATAROOT provided by test.sh -'$TST_DATAROOT/...' -or via C function 'tst_dataroot()' provided by libltp: - -[source,c] -------------------------------------------------------------------------------- -const char *dataroot = tst_dataroot(); -------------------------------------------------------------------------------- - -Datafiles can also be accessed as '$LTPROOT/testcases/data/$TCID/...', -but '$TST_DATAROOT' and 'tst_dataroot()' are preferred as these can be used -when running testcases directly in git tree as well as from install -location. - -The path is constructed according to these rules: - -1. if '$LTPROOT' is set, return '$LTPROOT/testcases/data/$TCID' -2. else if 'tst_tmpdir()' was called return '$STARTWD/datafiles' - (where '$STARTWD' is initial working directory as recorded by 'tst_tmpdir()') -3. else return '$CWD/datafiles' - -See 'testcases/commands/file/' for example. - -5.3 Subexecutables -^^^^^^^^^^^^^^^^^^ - -If your test needs to execute a binary, place it in the same directory as the -testcase and name the file starting with '${test_binary_name}_'. Once the -test is executed by the framework, the path to the directory with all LTP -binaries is added to the '$PATH' and you can execute it just by its name. - -TIP: If you need to execute such test from the LTP tree, you can add path to - current directory to '$PATH' manually with: 'PATH="$PATH:$PWD" ./foo01'. - -6 Test Contribution Checklist ------------------------------- - -NOTE: See also - https://github.com/linux-test-project/ltp/wiki/Maintainer-Patch-Review-Checklist[Maintainer Patch Review Checklist]. - -1. Test compiles and runs fine (check with `-i 10` too) -2. `make check` does not emit any warnings for the test you are working on - (hint: run it in the test's directory and/or use `make check-$TCID`) -3. The runtest entries are in place -4. Test binaries are added into corresponding '.gitignore' files -5. Patches apply over the latest git - -6.1 About .gitignore files -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There are numerous '.gitignore' files in the LTP tree. Usually there is a -'.gitignore' file per a group of tests. The reason for this setup is simple. -It's easier to maintain a '.gitignore' file per directory with tests, rather -than having single file in the project root directory. This way, we don't have -to update all the gitignore files when moving directories, and they get deleted -automatically when a directory with tests is removed. - -7 Testing pre-release kernel features -------------------------------------- - -Tests for features not yet in a mainline kernel release are accepted. However -they must only be added to the +staging+ runtest file. Once a feature is part -of the stable kernel ABI the associated test must be moved out of staging. - -This is primarily to help test kernel RCs by avoiding the need to download -separate LTP patchsets. - -8 LTP C And Shell Test API Comparison -------------------------------------- - -Comparison of -https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API] and -https://github.com/linux-test-project/ltp/wiki/Shell-Test-API[Shell Test API]. - -[options="header"] -|================================================================================ -| C API ('struct tst_test' members) | shell API ('$TST_*' environment variables) -| '.all_filesystems' | 'TST_ALL_FILESYSTEMS' -| '.bufs' | – -| '.caps' | – -| '.child_needs_reinit' | not applicable -| '.cleanup' | 'TST_CLEANUP' -| '.dev_extra_opts' | 'TST_DEV_EXTRA_OPTS' -| '.dev_fs_opts' | 'TST_DEV_FS_OPTS' -| '.dev_fs_type' | 'TST_FS_TYPE' -| '.dev_min_size' | not applicable -| '.format_device' | 'TST_FORMAT_DEVICE' -| '.max_runtime' | – -| '.min_cpus' | not applicable -| '.min_kver' | 'TST_MIN_KVER' -| '.min_mem_avail' | not applicable -| '.mnt_flags' | 'TST_MNT_PARAMS' -| '.min_swap_avail' | not applicable -| '.mntpoint', '.mnt_data' | 'TST_MNTPOINT' -| '.mount_device' | 'TST_MOUNT_DEVICE' -| '.needs_cgroup_ctrls' | – -| '.needs_checkpoints' | 'TST_NEEDS_CHECKPOINTS' -| '.needs_cmds' | 'TST_NEEDS_CMDS' -| '.needs_devfs' | – -| '.needs_device' | 'TST_NEEDS_DEVICE' -| '.needs_drivers' | 'TST_NEEDS_DRIVERS' -| '.needs_kconfigs' | 'TST_NEEDS_KCONFIGS' -| '.needs_overlay' | -| '.needs_rofs' | – -| '.needs_root' | 'TST_NEEDS_ROOT' -| '.needs_tmpdir' | 'TST_NEEDS_TMPDIR' -| '.options' | 'TST_PARSE_ARGS', 'TST_OPTS' -| '.resource_files' | – -| '.restore_wallclock' | not applicable -| '.sample' | – -| '.save_restore' | – -| '.scall' | not applicable -| '.setup' | 'TST_SETUP' -| '.skip_filesystems' | 'TST_SKIP_FILESYSTEMS' -| '.skip_in_compat' | – -| '.skip_in_lockdown' | 'TST_SKIP_IN_LOCKDOWN' -| '.skip_in_secureboot' | 'TST_SKIP_IN_SECUREBOOT' -| '.supported_archs' | not applicable -| '.tags' | – -| '.taint_check' | – -| '.tcnt' | 'TST_CNT' -| '.tconf_msg' | not applicable -| '.test', '.test_all' | 'TST_TESTFUNC' -| '.test_variants' | – -| '.timeout' | 'TST_TIMEOUT' -| '.tst_hugepage' | not applicable -| .format_device | 'TST_DEVICE' -| not applicable | 'TST_NEEDS_KCONFIGS_IFS' -| not applicable | 'TST_NEEDS_MODULE' -| not applicable | 'TST_POS_ARGS' -| not applicable | 'TST_USAGE' -|================================================================================ diff --git a/doc/User-Guidelines.asciidoc b/doc/User-Guidelines.asciidoc deleted file mode 100644 index 8f2418df..00000000 --- a/doc/User-Guidelines.asciidoc +++ /dev/null @@ -1,68 +0,0 @@ -LTP User Guidelines -=================== - -For compiling, installing and running the tests see `README.md`. -For running LTP network tests see `testcases/network/README.md`. - -1. Library environment variables --------------------------------- - -|============================================================================== -| 'KCONFIG_PATH' | The path to the kernel config file, (if not set, it tries - the usual paths '/boot/config-RELEASE' or '/proc/config.gz'). -| 'KCONFIG_SKIP_CHECK' | Skip kernel config check if variable set (not set by default). -| 'LTPROOT' | Prefix for installed LTP. **Should be always set** - as some tests need it for path to test data files - ('LTP_DATAROOT'). LTP is by default installed into '/opt/ltp'. -| 'LTP_COLORIZE_OUTPUT' | By default LTP colorizes it's output unless it's redirected - to a pipe or file. Force colorized output behaviour: - 'y' or '1': always colorize, 'n' or '0': never colorize. -| 'LTP_DEV' | Path to the block device to be used - (C: '.needs_device = 1', shell: 'TST_NEEDS_DEVICE=1'). -| 'LTP_SINGLE_FS_TYPE' | Testing only - specifies filesystem instead all - supported (for tests with '.all_filesystems'). -| 'LTP_DEV_FS_TYPE' | Filesystem used for testing (default: 'ext2'). -| 'LTP_TIMEOUT_MUL' | Multiplies timeout, must be number >= 0.1 (> 1 is useful for - slow machines to avoid unexpected timeout). - Variable is also used in shell tests, but ceiled to int. -| 'LTP_RUNTIME_MUL' | Multiplies maximal test iteration runtime. Tests - that run for more than a second or two are capped on - runtime. You can scale the default runtime both up - and down with this multiplier. NOTE: Not yet implemented - in shell API. -| 'LTP_VIRT_OVERRIDE' | Overrides virtual machine detection in the test - library. Setting it to empty string tell the library - that system is not a virtual machine. Other possible - values are 'kvm', 'xen', 'zvm' and 'microsoft' that - describe different types supervisors. -| 'PATH' | It's required to addjust path: - `PATH="$PATH:$LTPROOT/testcases/bin"` -| 'TMPDIR' | Base directory for template directory (C: '.needs_tmpdir = 1' - and others, which imply it, shell: 'TST_NEEDS_TMPDIR=1'). - Must be an absolute path (default: '/tmp'). -| 'TST_NO_CLEANUP' | Disable running test cleanup (defined in 'TST_CLEANUP'). -|============================================================================== - - -2. Test execution time and timeout ----------------------------------- - -The limit on how long a test can run does compose of two parts max_runtime and -timeout. The limit does apply to a single test variant, that means for example -that tests that run for all available filesystems will apply this limit for a -single filesystem only. - -The max_runtime is a cap on how long the run() function can take, for most -testcases this part is set to zero. For tests that do run for more than a -second or two the max_runtime has to be defined and the run() function has to -check actively how much runtime is left. - -Test runtime can be scaled up and down with 'LTP_RUNTIME_MUL' environment -variable or set on a commandline by the '-I' parameter. Hoewever be vary that -setting the runtime too low will cause long running tests to exit prematurely -possibly before the have a chance actually test anyting. - -The timeout part is a limit for the test setup and cleanup and also safety -margin for the runtime accounting. It's currently set to 30 seconds but may -change later. If your target machine is too slow it can be scaled up with the -'LTP_TIMEOUT_MUL' environment variable. diff --git a/doc/_static/custom.css b/doc/_static/custom.css new file mode 100644 index 00000000..c5816a55 --- /dev/null +++ b/doc/_static/custom.css @@ -0,0 +1,9 @@ +/* set multiline tables cells */ +.wy-table-responsive table td { + white-space: normal; +} + +/* remove margin for multiline cells */ +.rst-content table td div.line-block { + margin-bottom: 0; +} diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 00000000..d767969b --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,542 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +import os +import re +import json +import socket +import urllib.request +import sphinx + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'Linux Test Project' +copyright = '2024, Linux Test Project' +author = 'Linux Test Project' +release = '1.0' +ltp_repo = 'https://github.com/linux-test-project/ltp' +ltp_repo_base_url = f"{ltp_repo}/tree/master" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'linuxdoc.rstKernelDoc', + 'sphinxcontrib.spelling', + 'sphinx.ext.autosectionlabel', + 'sphinx.ext.extlinks', +] + +exclude_patterns = ["html*", '_static*', '.venv*'] +extlinks = { + 'repo': (f'{ltp_repo}/%s', '%s'), + 'master': (f'{ltp_repo}/blob/master/%s', '%s'), + 'git_man': ('https://git-scm.com/docs/git-%s', 'git %s'), + 'man2': ('https://man7.org/linux/man-pages/man2/%s.2.html', '%s(2)'), + # TODO: allow 2nd parameter to show page description instead of plain URL + 'kernel_doc': ('https://docs.kernel.org/%s.html', 'https://docs.kernel.org/%s.html'), + 'kernel_tree': ('https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/%s', '%s'), +} + +spelling_lang = "en_US" +spelling_warning = True +spelling_exclude_patterns = ['users/stats.rst'] +spelling_word_list_filename = "spelling_wordlist" + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] + + +def generate_syscalls_stats(_): + """ + Generate statistics for syscalls. We fetch the syscalls list from the kernel + sources, then we compare it with testcases/kernel/syscalls folder and + generate a file that is included in the statistics documentation section. + """ + output = '_static/syscalls.rst' + + # sometimes checking testcases/kernel/syscalls file names are not enough, + # because in some cases (i.e. io_ring) syscalls are tested, but they are + # part of a more complex scenario. In the following list, we define syscalls + # which we know they are 100% tested already. + ltp_syscalls_path = "testcases/kernel/syscalls" + white_list = { + 'bpf': f'{ltp_syscalls_path}/bpf', + 'epoll_pwait2': f'{ltp_syscalls_path}/epoll_pwait', + 'fadvise64': f'{ltp_syscalls_path}/fadvise', + 'fanotify_init': f'{ltp_syscalls_path}/fanotify', + 'fanotify_mark': f'{ltp_syscalls_path}/fanotify', + 'file_getattr': f'{ltp_syscalls_path}/file_attr', + 'file_setattr': f'{ltp_syscalls_path}/file_attr', + 'futex': f'{ltp_syscalls_path}/futex', + 'getdents64': f'{ltp_syscalls_path}/gettdents', + 'inotify_add_watch': f'{ltp_syscalls_path}/inotify', + 'inotify_init': f'{ltp_syscalls_path}/inotify', + 'inotify_rm_watch': f'{ltp_syscalls_path}/inotify', + 'io_uring_enter': f'{ltp_syscalls_path}/io_uring', + 'io_uring_register': f'{ltp_syscalls_path}/io_uring', + 'io_uring_setup': f'{ltp_syscalls_path}/io_uring', + 'landlock_add_rule': f'{ltp_syscalls_path}/landlock', + 'landlock_create_ruleset': f'{ltp_syscalls_path}/landlock', + 'landlock_restrict_self': f'{ltp_syscalls_path}/landlock', + 'lsetxattr': f'{ltp_syscalls_path}/lgetxattr', + 'newfstatat': f'{ltp_syscalls_path}/fstatat', + 'pkey_alloc': f'{ltp_syscalls_path}/pkeys', + 'pkey_free': f'{ltp_syscalls_path}/pkeys', + 'pkey_mprotect': f'{ltp_syscalls_path}/pkeys', + 'prlimit64': f'{ltp_syscalls_path}/getrlimit', + 'pread64': f'{ltp_syscalls_path}/pread', + 'pselect6': f'{ltp_syscalls_path}/pselect', + 'pwrite64': f'{ltp_syscalls_path}/pwrite', + 'quotactl_fd': f'{ltp_syscalls_path}/quotactl', + 'rt_sigpending': f'{ltp_syscalls_path}/sigpending', + 'semtimedop': f'{ltp_syscalls_path}/ipc/semop', + 'sethostname': f'{ltp_syscalls_path}/sethostname' + } + + # populate with not implemented, reserved, unmaintained syscalls defined + # inside the syscalls file + black_list = [ + '_newselect', + '_sysctl', + 'afs_syscall', + 'cachectl', + 'create_module', + 'get_kernel_syms', + 'getmsg', + 'getpmsg', + 'mq_getsetattr', + 'nfsservctl', + 'putmsg', + 'putpmsg', + 'query_module', + 'reserved177', + 'reserved193', + 'restart_syscall', + 'rseq', + 'sysmips', + 'vserver', + ] + + # fetch syscalls file + syscalls_url = "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/arch/mips/kernel/syscalls" + error = False + try: + socket.setdefaulttimeout(3) + + # kernel.org doesn't always allow to download syscalls file, so we need + # to emulate a different client in order to download it. Browser + # emulation might fail due to captcha request and for this reason we + # use the 'curl' command instead + req = urllib.request.Request( + f"{syscalls_url}/syscall_n64.tbl", + headers={'User-Agent': 'curl/8.6.0'}) + + with urllib.request.urlopen(req) as response: + with open("syscalls.tbl", 'wb') as out_file: + out_file.write(response.read()) + except urllib.error.URLError as err: + error = True + logger = sphinx.util.logging.getLogger(__name__) + msg = f"Can't download syscall_n64.tbl from kernel sources ({err})" + logger.warning(msg) + + with open(output, 'w+', encoding='utf-8') as stats: + stats.write(f".. warning::\n\n {msg}") + + if error: + return + + text = [ + 'Syscalls\n', + '--------\n\n', + ] + + # collect all available kernel syscalls + regexp = re.compile(r'\d+\s+n64\s+(?P\w+)\s+\w+') + ker_syscalls = [] + with open("syscalls.tbl", 'r', encoding='utf-8') as data: + for line in data: + match = regexp.search(line) + if not match: + continue + + ker_syscalls.append(match.group('syscall')) + + # collect all LTP tested syscalls + name_patterns = [ + re.compile(r'(?P[a-zA-Z_]+[^_])\d{2}\.c'), + re.compile(r'(?P[a-zA-Z_]+[1-9])_\d{2}\.c'), + ] + ltp_syscalls = {} + for dirpath, _, files in os.walk(f'../{ltp_syscalls_path}'): + for myfile in files: + match = None + for pattern in name_patterns: + match = pattern.search(myfile) + if match: + break + + if not match: + continue + + # we need to use relative path from the project root + path = dirpath.replace('../', '') + name = match.group('name') + + ltp_syscalls[name] = f'{ltp_repo_base_url}/{path}' + + # compare kernel syscalls with LTP tested syscalls + syscalls = {} + for kersc in ker_syscalls: + if kersc in black_list: + continue + + if kersc not in syscalls: + if kersc in white_list: + syscalls[kersc] = f'{ltp_repo_base_url}/{white_list[kersc]}' + continue + + syscalls[kersc] = None + + for ltpsc, ltpsp in ltp_syscalls.items(): + if ltpsc == kersc: + syscalls[kersc] = ltpsp + + # generate the statistics file + tested_syscalls = [key for key, val in syscalls.items() if val is not None] + text.append('syscalls which are tested under ' + ':master:`testcases/kernel/syscalls`:\n\n') + text.append(f'* kernel syscalls: {len(ker_syscalls)}\n') + text.append(f'* tested syscalls: {len(tested_syscalls)}\n\n') + + # create tested/untested syscalls table + index_tested = 0 + table_tested = [ + 'Tested syscalls\n', + '~~~~~~~~~~~~~~~\n\n', + '.. list-table::\n', + ' :header-rows: 0\n\n', + ] + + index_untest = 0 + table_untest = [ + 'Untested syscalls\n', + '~~~~~~~~~~~~~~~~~\n\n', + '.. list-table::\n', + ' :header-rows: 0\n\n', + ] + + max_columns = 3 + + for sysname, path in syscalls.items(): + if path is not None: + if (index_tested % max_columns) == 0: + table_tested.append(f' * - `{sysname} <{path}>`_\n') + else: + table_tested.append(f' - `{sysname} <{path}>`_\n') + + index_tested += 1 + else: + if (index_untest % max_columns) == 0: + table_untest.append(f' * - {sysname}\n') + else: + table_untest.append(f' - {sysname}\n') + + index_untest += 1 + + left = index_tested % max_columns + if left > 0: + for _ in range(0, max_columns - left): + table_tested.append(' -\n') + + left = index_untest % max_columns + if left > 0: + for _ in range(0, max_columns - left): + table_untest.append(' -\n') + + text.extend(table_tested) + text.append('\n') + text.extend(table_untest) + + # write the file + with open(output, 'w+', encoding='utf-8') as stats: + stats.writelines(text) + + +def _generate_tags_table(tags): + """ + Generate the tags table from tags hash. + """ + supported_url_ref = { + "linux-git": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=", + "linux-stable-git": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=", + "glibc-git": "https://sourceware.org/git/?p=glibc.git;a=commit;h=", + "musl-git": "https://git.musl-libc.org/cgit/musl/commit/src/linux/clone.c?id=", + "CVE": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-", + } + + table = [ + '.. list-table::', + ' :header-rows: 1', + '', + ' * - Tag', + ' - Info', + ] + + for tag in tags: + tag_key = tag[0] + tag_val = tag[1] + + tag_url = supported_url_ref.get(tag_key, None) + if tag_url: + tag_val = f'`{tag_val} <{tag_url}{tag_val}>`_' + + table.extend([ + f' * - :c:struct:`{tag_key} `', + f' - {tag_val}', + ]) + + return table + + +def _generate_options_table(options): + """ + Generate the options table from the options hash. + """ + table = [ + '.. list-table::', + ' :header-rows: 1', + '', + ' * - Option', + ' - Description', + ] + + for opt in options: + if not isinstance(opt, list): + table.clear() + break + + key = opt[0] + val = opt[2] + + if key.endswith(':'): + key = key[:-1] if key.endswith(':') else key + + key = f'-{key}' + + table.extend([ + f' * - {key}', + f' - {val}', + ]) + + return table + + +def _generate_table_cell(key, values): + """ + Generate a cell which can be multiline if value is a list. + """ + cell = [] + key = f' :c:struct:`{key} `' + + if len(values) > 1: + cell.extend([ + f' * - {key}', + f' - | {values[0]}', + ]) + + for item in values[1:]: + cell.append(f' | {item}') + else: + cell.extend([ + f' * - {key}', + f' - {values[0]}', + ]) + + return cell + + +def _generate_setup_table(keys): + """ + Generate the table with test setup configuration. + """ + exclude = [ + # following keys are already handled + 'options', + 'runtime', + 'timeout', + 'fname', + 'doc', + # following keys don't need to be shown + 'child_needs_reinit', + 'needs_checkpoints', + 'forks_child', + 'tags', + ] + my_keys = {k: v for k, v in keys.items() if k not in exclude} + if len(my_keys) == 0: + return [] + + table = [ + '.. list-table::', + ' :header-rows: 1', + '', + ' * - Key', + ' - Value', + ] + + values = [] + + for key, value in my_keys.items(): + if key in exclude: + continue + + values.clear() + + if key == 'ulimit': + for item in value: + values.append(f'{item[0]} : {item[1]}') + elif key == 'hugepages': + if len(value) == 1: + values.append(f'{value[0]}') + else: + values.append(f'{value[0]}, {value[1]}') + elif key == 'filesystems': + for params in value: + for pkey, pval in params.items(): + if pkey == "type": + values.append(f"- {pval}") + else: + values.append(f" {pkey}: {pval}") + elif key == "save_restore": + for item in value: + values.append(item[0]) + else: + if isinstance(value, list): + values.extend(value) + else: + values.append(value) + + if values: + table.extend(_generate_table_cell(key, values)) + + return table + + +def generate_test_catalog(_): + """ + Generate the test catalog from ltp.json metadata file. + """ + output = '_static/tests.rst' + metadata_file = '../metadata/ltp.json' + text = [ + '.. warning::', + ' The following catalog has been generated using LTP metadata', + ' which is including only tests using the new :ref:`LTP C API`.', + '' + ] + + metadata = None + with open(metadata_file, 'r', encoding='utf-8') as data: + metadata = json.load(data) + + timeout_def = metadata['defaults']['timeout'] + regexp = re.compile(r'^\[([A-Za-z][\w\W]+)\]') + + for test_name, conf in sorted(metadata['tests'].items()): + text.extend([ + f'{test_name}', + len(test_name) * '-' + ]) + + # source url location + test_fname = conf.get('fname', None) + if test_fname: + text.extend([ + '', + f"`source <{ltp_repo_base_url}/{test_fname}>`__", + '' + ]) + + # test description + desc = conf.get('doc', None) + if desc: + desc_text = [] + for line in desc: + line = regexp.sub(r'**\1**', line) + desc_text.append(line) + + text.extend([ + '\n'.join(desc_text), + ]) + + # timeout information + timeout = conf.get('timeout', None) + if timeout: + text.extend([ + '', + f'Test timeout is {timeout} seconds.', + ]) + else: + text.extend([ + '', + f'Test timeout defaults is {timeout_def} seconds.', + ]) + + # runtime information + runtime = conf.get('runtime', None) + if runtime: + text.extend([ + f'Maximum runtime is {runtime} seconds.', + '' + ]) + else: + text.append('') + + # options information + opts = conf.get('options', None) + if opts: + text.append('') + text.extend(_generate_options_table(opts)) + text.append('') + + # tags information + tags = conf.get('tags', None) + if tags: + text.append('') + text.extend(_generate_tags_table(tags)) + text.append('') + + # parse struct tst_test content + text.append('') + text.extend(_generate_setup_table(conf)) + text.append('') + + # small separator between tests + text.extend([ + '', + '.. raw:: html', + '', + '
', + '', + ]) + + with open(output, 'w+', encoding='utf-8') as new_tests: + new_tests.write('\n'.join(text)) + + +def setup(app): + """ + Setup the current documentation, using self generated data and graphics + customizations. + """ + app.add_css_file('custom.css') + app.connect('builder-inited', generate_syscalls_stats) + app.connect('builder-inited', generate_test_catalog) diff --git a/doc/developers/api_c_tests.rst b/doc/developers/api_c_tests.rst new file mode 100644 index 00000000..d1464598 --- /dev/null +++ b/doc/developers/api_c_tests.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later +.. Copyright (c) Linux Test Project, 2024 + +.. Include headers in this file with: +.. .. kernel-doc:: ../../include/tst_test.h + +LTP C API +========= + +Core LTP API +------------ +.. kernel-doc:: ../../include/tst_res_flags.h +.. kernel-doc:: ../../include/tst_test.h + +Test macros +----------- +.. kernel-doc:: ../../include/tst_test_macros.h + +Capabilities +------------ +.. kernel-doc:: ../../include/tst_capability.h + +Checkpoints +----------- + +.. kernel-doc:: ../../include/tst_checkpoint.h + +Crypto +------ +.. kernel-doc:: ../../include/tst_crypto.h +.. kernel-doc:: ../../include/tst_af_alg.h + +Guarded buffers +--------------- +.. kernel-doc:: ../../include/tst_buffers.h + +Kernel +------ +.. kernel-doc:: ../../include/tst_kernel.h + +NUMA +---- +.. kernel-doc:: ../../include/tst_numa.h + +Option parsing +-------------- + +.. kernel-doc:: ../../include/tst_parse.h + +Temporary directory +------------------- +.. kernel-doc:: ../../include/tst_tmpdir.h + +LTP libraries +------------- +.. kernel-doc:: ../../include/libswap.h diff --git a/doc/developers/api_kvm_tests.rst b/doc/developers/api_kvm_tests.rst new file mode 100644 index 00000000..eb71093f --- /dev/null +++ b/doc/developers/api_kvm_tests.rst @@ -0,0 +1,485 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. Include headers in this file with: +.. .. kernel-doc:: ../../include/tst_test.h + +Developing using KVM API +======================== + +Testing KVM is more complex than other Linux features. Some KVM bugs allow +userspace code running inside the virtual machine to bypass (emulated) hardware +access restrictions and elevate its privileges inside the guest operating +system. The worst types of KVM bugs may even allow the guest code to crash or +compromise the physical host. KVM tests therefore need to be split into two +components – a KVM controller program running on the physical host and a guest +payload program running inside the VM. The cooperation of these two components +allows testing even of bugs that somehow cross the virtualization boundary. + +Basic KVM Test Structure +------------------------ + +KVM tests are simple C source files containing both the KVM controller code +and the guest payload code separated by ``#ifdef COMPILE_PAYLOAD`` preprocessor +condition. The file will be compiled twice: once to compile the payload part, +and once to compile the KVM controller part and embed the payload binary inside. +The result is a single self-contained binary that will execute the embedded +payload inside a KVM virtual machine and print results in the same format as +a normal LTP test. + +A KVM test source should start with ``#include "kvm_test.h"`` instead of the +usual ``tst_test.h``. The ``kvm_test.h`` header file will include the other basic +headers appropriate for the current compilation pass. Everything else in the +source file should be enclosed in ``#ifdef COMPILE_PAYLOAD ... #else ... #endif`` +condition, including any other header file includes. Note that the standard +LTP headers are not available in the payload compilation pass; only the KVM +guest library headers can be included. + +.. _example-kvm-test: + +.. rubric:: Example KVM Test + +.. code-block:: c + + #include "kvm_test.h" + + #ifdef COMPILE_PAYLOAD + + /* Guest payload code */ + + void main(void) + { + tst_res(TPASS, "Hello, world!"); + } + + #else /* COMPILE_PAYLOAD */ + + /* KVM controller code */ + + static struct tst_test test = { + .test_all = tst_kvm_run, + .setup = tst_kvm_setup, + .cleanup = tst_kvm_cleanup, + }; + + #endif /* COMPILE_PAYLOAD */ + +The KVM controller code is a normal LTP test and needs to define an instance +of ``struct tst_test`` with metadata and the usual setup, cleanup, and test +functions. Basic implementation of all three functions is provided by the KVM +host library. + +On the other hand, the payload is essentially a tiny kernel that will run +on bare virtual hardware. It cannot access any files, Linux syscalls, standard +library functions, etc., except for the small subset provided by the KVM guest +library. The payload code must define a ``void main(void)`` function which will +be the VM entry point of the test. + +KVM Host Library +---------------- + +The KVM host library provides helper functions for creating and running +a minimal KVM virtual machine. + +Data Structures +~~~~~~~~~~~~~~~ + +.. code-block:: c + + struct tst_kvm_instance { + int vm_fd, vcpu_fd; + struct kvm_run *vcpu_info; + size_t vcpu_info_size; + struct kvm_userspace_memory_region ram[MAX_KVM_MEMSLOTS]; + struct tst_kvm_result *result; + }; + +``struct tst_kvm_instance`` holds the file descriptors and memory buffers +of a single KVM virtual machine: + +* ``int vm_fd`` is the main VM file descriptor created by ``ioctl(KVM_CREATE_VM)`` +* ``int vcpu_fd`` is the virtual CPU file descriptor created by + ``ioctl(KVM_CREATE_VCPU)`` +* ``struct kvm_run *vcpu_info`` is the VCPU state structure created by + ``mmap(vcpu_fd)`` +* ``size_t vcpu_info_size`` is the size of ``vcpu_info`` buffer +* ``struct kvm_userspace_memory_region ram[MAX_KVM_MEMSLOTS]`` is the list + of memory slots defined in this VM. Unused memory slots have zero + in the ``userspace_addr`` field. +* ``struct tst_kvm_result *result`` is a buffer for passing test result data + from the VM to the controller program, mainly ``tst_res()``/``tst_brk()`` flags + and messages. + +.. code-block:: c + + struct tst_kvm_result { + int32_t result; + int32_t lineno; + uint64_t file_addr; + char message[0]; + }; + +``struct tst_kvm_result`` is used to pass test results and synchronization data +between the KVM guest and the controller program. Most often, it is used +to pass ``tst_res()`` and ``tst_brk()`` messages from the VM, but special values +can also be used to send control flow requests both ways. + +* ``int32_t result`` is the message type, either one of the ``TPASS``, ``TFAIL``, + ``TWARN``, ``TBROK``, ``TINFO`` flags or a special control flow value. Errno flags + are not supported. +* ``int32_t lineno`` is the line number for ``tst_res()``/``tst_brk()`` messages. +* ``uint64_t file_addr`` is the VM address of the filename string for + ``tst_res()``/``tst_brk()`` messages. +* ``char message[0]`` is the buffer for arbitrary message data, most often used + to pass ``tst_res()``/``tst_brk()`` message strings. + +Working with Virtual Machines +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The KVM host library provides default implementation of the setup, cleanup +and test functions for ``struct tst_test`` in cases where you do not need +to customize the VM configuration. You can either assign these functions +to the ``struct tst_test`` instance directly or call them from your own function +that does some additional steps. All three functions must be used together. + +* ``void tst_kvm_setup(void)`` +* ``void tst_kvm_run(void)`` +* ``void tst_kvm_cleanup(void)`` + +.. note:: ``tst_kvm_run()`` calls ``tst_free_all()``. Calling it will free all + previously allocated guarded buffers. + +* ``void tst_kvm_validate_result(int value)`` – Validates whether the value + returned in ``struct tst_kvm_result.result`` can be safely passed + to ``tst_res()`` or ``tst_brk()``. If the value is not valid, the controller + program will be terminated with an error. + +* ``uint64_t tst_kvm_get_phys_address(const struct tst_kvm_instance *inst, uint64_t addr)`` – + Converts pointer value (virtual address) from KVM virtual + machine ``inst`` to the corresponding physical address. Returns 0 if + the virtual address is not mapped to any physical address. If virtual memory + mapping is not enabled in the VM or not available on the architecture at all, this + function simply returns ``addr`` as is. + +* ``int tst_kvm_find_phys_memslot(const struct tst_kvm_instance *inst, uint64_t paddr)`` – + Returns index of the memory slot in KVM virtual machine + ``inst`` which contains the physical address ``paddr``. If the address is not + backed by a memory buffer, returns -1. + +* ``int tst_kvm_find_memslot(const struct tst_kvm_instance *inst, uint64_t addr)`` – + Returns index of the memory slot in KVM virtual machine + ``inst`` which contains the virtual address ``addr``. If the virtual address + is not mapped to a valid physical address backed by a memory buffer, + returns -1. + +* ``void *tst_kvm_get_memptr(const struct tst_kvm_instance *inst, uint64_t addr)`` – + Converts pointer value (virtual address) from KVM virtual + machine ``inst`` to host-side pointer. + +* ``void *tst_kvm_alloc_memory(struct tst_kvm_instance *inst, unsigned int slot, uint64_t baseaddr, size_t size, unsigned int flags)`` – + Allocates a guarded buffer of given ``size`` in bytes and installs it into specified memory ``slot`` + of the KVM virtual machine ``inst`` at base address ``baseaddr``. The buffer + will be automatically page aligned at both ends. See the kernel + documentation of ``KVM_SET_USER_MEMORY_REGION`` ioctl for a list of valid + ``flags``. Returns pointer to page-aligned beginning of the allocated buffer. + The actual requested ``baseaddr`` will be located at + ``ret + baseaddr % pagesize``. + +* ``struct kvm_cpuid2 *tst_kvm_get_cpuid(int sysfd)`` – Gets a list of supported + virtual CPU features returned by ``ioctl(KVM_GET_SUPPORTED_CPUID)``. + The argument must be an open file descriptor returned by ``open("/dev/kvm")``. + +* ``void tst_kvm_create_instance(struct tst_kvm_instance *inst, size_t ram_size)`` – + Creates and fully initializes a new KVM virtual machine + with at least ``ram_size`` bytes of memory. The VM instance info will be + stored in ``inst``. + +* ``int tst_kvm_run_instance(struct tst_kvm_instance *inst, int exp_errno)`` – + Executes the program installed in KVM virtual machine ``inst``. Any result + messages returned by the VM will be automatically printed to the controller + program output. Returns zero. If ``exp_errno`` is non-zero, the VM execution + syscall is allowed to fail with the ``exp_errno`` error code and + ``tst_kvm_run_instance()`` will return -1 instead of terminating the test. + +* ``void tst_kvm_destroy_instance(struct tst_kvm_instance *inst)`` – Deletes + the KVM virtual machine ``inst``. Note that the guarded buffers assigned + to the VM by ``tst_kvm_create_instance()`` or ``tst_kvm_alloc_memory()`` will + not be freed. + +The KVM host library does not provide any way to reset a VM instance back +to its initial state. Running multiple iterations of the test requires destroying +the old VM instance and creating a new one. Otherwise, the VM will exit +without reporting any results on the second iteration, and the test will fail. +The ``tst_kvm_run()`` function handles this issue correctly. + +KVM Guest Library +----------------- + +The KVM guest library provides a minimal implementation of both the LTP +test library and the standard C library functions. Do not try to include +the usual LTP or C headers in guest payload code; it will not work. + +Standard C Functions +~~~~~~~~~~~~~~~~~~~~ + +``#include "kvm_test.h"`` + +The functions listed below are implemented according to the C standard: + +* ``void *memset(void *dest, int val, size_t size)`` +* ``void *memzero(void *dest, size_t size)`` +* ``void *memcpy(void *dest, const void *src, size_t size)`` +* ``char *strcpy(char *dest, const char *src)`` +* ``char *strcat(char *dest, const char *src)`` +* ``size_t strlen(const char *str)`` + +LTP Library Functions +~~~~~~~~~~~~~~~~~~~~~ + +``#include "kvm_test.h"`` + +The KVM guest library currently provides the LTP functions for reporting test +results. All standard result flags except for ``T*ERRNO`` are supported +with the same rules as usual. However, printf-like formatting is not +implemented yet. + +* ``void tst_res(int result, const char *message)`` +* ``void tst_brk(int result, const char *message) __attribute__((noreturn))`` + +A handful of useful macros is also available: + +* ``TST_TEST_TCONF(message)`` – Generates a test program that will simply print + a ``TCONF`` message and exit. This is useful when the real test cannot be + built due to missing dependencies or architecture limitations. + +* ``ARRAY_SIZE(arr)`` – Returns the number of items in statically allocated + array ``arr``. + +* ``LTP_ALIGN(x, a)`` – Aligns integer ``x`` to be a multiple of ``a``, which + must be a power of 2. + +Architecture Independent Functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``#include "kvm_test.h"`` + +Memory management in the KVM guest library currently uses only a primitive linear +buffer for memory allocation. There are no checks whether the VM can allocate +more memory, and the already allocated memory cannot be freed. + +* ``void *tst_heap_alloc(size_t size)`` – Allocates a block of memory on the heap. + +* ``void *tst_heap_alloc_aligned(size_t size, size_t align)`` – Allocates + a block of memory on the heap with the starting address aligned to a given + value. + +x86 Specific Functions +~~~~~~~~~~~~~~~~~~~~~~ + +``#include "kvm_test.h"`` +``#include "kvm_x86.h"`` + +* ``struct kvm_interrupt_frame`` – Opaque architecture-dependent structure which holds + interrupt frame information. Use the functions below to get individual values: + +* ``uintptr_t kvm_get_interrupt_ip(const struct kvm_interrupt_frame *ifrm)`` – + Gets instruction pointer value from the interrupt frame structure. This may be + the instruction which caused an interrupt or the one immediately after, + depending on the interrupt vector semantics. + +* ``int (*tst_interrupt_callback)(void *userdata, struct kvm_interrupt_frame *ifrm, unsigned long errcode)`` – + Interrupt handler callback prototype. When an interrupt occurs, the assigned callback function + will be passed the ``userdata`` pointer that was given + to ``tst_set_interrupt_callback()``, interrupt frame ``ifrm`` and the error + code ``errcode`` defined by the interrupt vector semantics. If the interrupt + vector does not generate an error code, ``errcode`` will be set to zero. + The callback function must return 0 if the interrupt was successfully + handled and test execution should resume. A non-zero return value means that + the interrupt could not be handled and the test will terminate with an error. + +* ``void tst_set_interrupt_callback(unsigned int vector, tst_interrupt_callback func, void *userdata)`` – + Registers a new interrupt handler callback function ``func`` for interrupt ``vector``. The ``userdata`` + argument is an arbitrary pointer that will be passed to ``func()`` every time + it gets called. The previous interrupt handler callback will be removed. + Setting ``func`` to ``NULL`` will remove any existing interrupt handler + from ``vector``, and the interrupt will become a fatal error. + +.. code-block:: c + + struct page_table_entry_pae { + unsigned int present: 1; + unsigned int writable: 1; + unsigned int user_access: 1; + unsigned int write_through: 1; + unsigned int disable_cache: 1; + unsigned int accessed: 1; + unsigned int dirty: 1; + unsigned int page_type: 1; + unsigned int global: 1; + unsigned int padding: 3; + uint64_t address: 40; + unsigned int padding2: 7; + unsigned int prot_key: 4; + unsigned int noexec: 1; + } __attribute__((__packed__)); + + struct kvm_cpuid { + unsigned int eax, ebx, ecx, edx; + }; + + struct kvm_cregs { + unsigned long cr0, cr2, cr3, cr4; + }; + + struct kvm_sregs { + uint16_t cs, ds, es, fs, gs, ss; + }; + +``struct page_table_entry_pae`` is the page table entry structure for PAE and +64-bit paging modes. See Intel® 64 and IA-32 Architectures Software +Developer's Manual, Volume 3, Chapter 4 for an explanation of the fields. + +* ``uintptr_t kvm_get_page_address_pae(const struct page_table_entry_pae *entry)`` + – Returns the physical address of the memory page referenced by the given + page table ``entry``. Depending on memory mapping changes done by the test, + the physical address may not be a valid pointer. The caller must determine + whether the address points to another page table entry or a data page, using + the known position in page table hierarchy and ``entry->page_type``. Returns + zero if the ``entry`` does not reference any memory page. + +* ``void kvm_set_segment_descriptor(struct segment_descriptor *dst, uint64_t baseaddr, uint32_t limit, unsigned int flags)`` - + Fills the ``dst`` segment descriptor with given values. The maximum value + of ``limit`` is ``0xfffff`` (inclusive) regardless of ``flags``. + +* ``void kvm_parse_segment_descriptor(struct segment_descriptor *src, uint64_t *baseaddr, uint32_t *limit, unsigned int *flags)`` - + Parses data in the ``src`` segment descriptor and copies them to variables + pointed to by the other arguments. Any parameter except the first one can + be ``NULL``. + +* ``int kvm_find_free_descriptor(const struct segment_descriptor *table, size_t size)`` - + Finds the first segment descriptor in ``table`` which does not have + the ``SEGFLAG_PRESENT`` bit set. The function handles double-size descriptors + correctly. Returns the index of the first available descriptor or -1 if all + ``size`` descriptors are taken. + +* ``unsigned int kvm_create_stack_descriptor(struct segment_descriptor *table, size_t tabsize, void *stack_base)`` - + A convenience function for registering a stack segment descriptor. It will + automatically find a free slot in ``table`` and fill the necessary flags. + The ``stack_base`` pointer must point to the bottom of the stack. + +* ``void kvm_get_cpuid(unsigned int eax, unsigned int ecx, struct kvm_cpuid *buf)`` – + Executes the CPUID instruction with the given + ``eax`` and ``ecx`` arguments and stores the results in ``buf``. + +* ``void kvm_read_cregs(struct kvm_cregs *buf)`` – Copies the current values + of control registers to ``buf``. + +* ``void kvm_read_sregs(struct kvm_sregs *buf)`` - Copies the current values + of segment registers to ``buf``. + +* ``uint64_t kvm_rdmsr(unsigned int msr)`` – Returns the current value + of model-specific register ``msr``. + +* ``void kvm_wrmsr(unsigned int msr, uint64_t value)`` – Stores ``value`` + into model-specific register ``msr``. + +* ``void kvm_exit(void) __attribute__((noreturn))`` – Terminates the test. + Similar to calling ``exit(0)`` in a regular LTP test, although ``kvm_exit()`` + will terminate only one iteration of the test, not the whole host process. + +See Intel® 64 and IA-32 Architectures Software Developer's Manual +for documentation of standard and model-specific x86 registers. + +AMD SVM Helper Functions +~~~~~~~~~~~~~~~~~~~~~~~~ + +``#include "kvm_test.h"`` +``#include "kvm_x86.h"`` +``#include "kvm_x86_svm.h"`` + +The KVM guest library provides basic helper functions for creating and running +nested virtual machines using the AMD SVM technology. + +.. _example-code-to-execute-nested-vm: + +.. rubric:: Example Code to Execute Nested VM + +.. code-block:: c + + int guest_main(void) + { + ... + return 0; + } + + void main(void) + { + struct kvm_svm_vcpu *vm; + + kvm_init_svm(); + vm = kvm_create_svm_vcpu(guest_main, 1); + kvm_svm_vmrun(vm); + } + +* ``int kvm_is_svm_supported(void)`` - Returns a non-zero value if the CPU + supports AMD SVM; otherwise, returns 0. + +* ``int kvm_get_svm_state(void)`` - Returns a non-zero value if SVM is currently + enabled; otherwise, returns 0. + +* ``void kvm_set_svm_state(int enabled)`` - Enables or disables SVM according + to the argument. If SVM is disabled by the host or not supported, the test will exit + with ``TCONF``. + +* ``void kvm_init_svm(void)`` - Enables and fully initializes SVM, including + allocating and setting up the host save area VMCB. If SVM is disabled by the host or + not supported, the test will exit with ``TCONF``. + +* ``struct kvm_vmcb *kvm_alloc_vmcb(void)`` - Allocates a new VMCB structure + with correct memory alignment and fills it with zeroes. + +* ``void kvm_vmcb_set_intercept(struct kvm_vmcb *vmcb, unsigned int id, unsigned int state)`` - + Sets SVM intercept bit ``id`` to the given ``state``. + +* ``void kvm_init_guest_vmcb(struct kvm_vmcb *vmcb, uint32_t asid, uint16_t ss, void *rsp, int (*guest_main)(void))`` - + Initializes a new SVM virtual machine. The ``asid`` parameter is the nested + page table ID. The ``ss`` and ``rsp`` parameters set the stack segment and stack + pointer values, respectively. The ``guest_main`` parameter sets the code entry + point of the virtual machine. All control registers, segment registers + (except stack segment register), GDTR and IDTR will be copied + from the current CPU state. + +* ``struct kvm_svm_vcpu *kvm_create_svm_vcpu(int (*guest_main)(void), int alloc_stack)`` - + A convenience function for allocating and initializing a new SVM virtual CPU. + The ``guest_main`` parameter is passed to ``kvm_init_guest_vmcb()``; + the ``alloc_stack`` parameter controls whether a new 8KB stack will be + allocated and registered in GDT. Interception will be enabled for ``VMSAVE`` + and ``HLT`` instructions. If you set ``alloc_stack`` to zero, you must configure + the stack segment register and stack pointer manually. + +* ``void kvm_svm_vmrun(struct kvm_svm_vcpu *cpu)`` - Starts or continues execution + of a nested virtual machine. Be aware that FPU state is not saved. Do not use + floating point types or values in nested guest code. Also, do not use + ``tst_res()`` or ``tst_brk()`` functions in nested guest code. + +See AMD64 Architecture Programmer's Manual Volume 2 for documentation +of the Secure Virtual Machine (SVM) technology. + +KVM Guest Environment +--------------------- + +KVM guest payload execution begins with bootstrap code which will perform +the minimal guest environment setup required for running C code: + +* Activates the appropriate CPU execution mode (IA-32 protected mode + on 32-bit x86 or the 64-bit mode on x86_64). +* Creates an identity mapping (virtual address = physical address) of the lower + 2GB memory region, even if parts of the region are not backed by any host + memory buffers. The memory region above the 2GB threshold is left unmapped + except for one memory page reserved for the ``struct tst_kvm_result`` buffer. +* Initializes an 8KB stack. +* Installs default interrupt handlers for standard CPU exception vectors. + +When the environment setup is complete, bootstrap will call the ``void main(void)`` +function implemented by the test program. To finish execution of the guest payload, +the test can either return from the ``main()`` function or call ``kvm_exit()`` +at any point. diff --git a/doc/developers/api_network_tests.rst b/doc/developers/api_network_tests.rst new file mode 100644 index 00000000..3e487d7f --- /dev/null +++ b/doc/developers/api_network_tests.rst @@ -0,0 +1,7 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. Include headers in this file with: +.. .. kernel-doc:: ../../include/tst_test.h + +Developing using network API +============================ diff --git a/doc/developers/api_shell_tests.rst b/doc/developers/api_shell_tests.rst new file mode 100644 index 00000000..b6e8560d --- /dev/null +++ b/doc/developers/api_shell_tests.rst @@ -0,0 +1,4 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +LTP shell API +============= diff --git a/doc/developers/build_system.rst b/doc/developers/build_system.rst new file mode 100644 index 00000000..8dca5720 --- /dev/null +++ b/doc/developers/build_system.rst @@ -0,0 +1,209 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Build system +============ + +The following document briefly describes the steps and methodologies used for +the new and improved Makefile system. + +The Problem +----------- + +The problem with the old Makefile system is that it was very difficult to +maintain and it lacked any sense of formal structure, thus developing for LTP +and including new targets was more difficult than it should have been +(maintenance). Furthermore, proper option-based cross-compilation was +impossible due to the fact that the Makefiles didn't support a prefixing +system, and the appropriate implicit / static rules hadn't been configured to +compile into multiple object directories for out-of-tree build support (ease of +use / functionality). Finally, there wasn't a means to setup dependencies +between components, such that if a component required ``libltp.a`` in order to +compile, it would go off and compile ``libltp.a`` first (ease of use). + +These items needed to be fixed to reduce maintenance nightmares for the +development community contributing to LTP, and the project maintainers. + +Design +------ + +The system was designed such that including a single GNU Makefile compatible +set in each new directory component is all that's essentially required to +build the system. + +Say you had a directory like the following (with ``.c`` files in them which +directly tie into applications, e.g. baz.c -> baz): + +.. code-block:: + + .../foo/ + |--> Makefile + | + --> bar/ + | + --> Makefile + | + --> baz.c + +.. code-block:: make + :caption: .../foo/Makefile + + # SPDX-License-Identifier: GPL-2.0-or-later + + top_srcdir ?= .. + + include $(top_srcdir)/include/mk/env_pre.mk + include $(top_srcdir)/include/mk/generic_trunk_target.mk + +.. code-block:: make + :caption: .../foo/bar/Makefile + + # SPDX-License-Identifier: GPL-2.0-or-later + + top_srcdir ?= ../.. + + include $(top_srcdir)/include/mk/env_pre.mk + include $(top_srcdir)/include/mk/generic_leaf_target.mk + +Kernel Modules +-------------- + +Some of the tests need to build kernel modules, happily LTP has +infrastructure for this. + +.. code-block:: make + + ifneq ($(KERNELRELEASE),) + + obj-m := module01.o + + else + + top_srcdir ?= ../../../.. + include $(top_srcdir)/include/mk/testcases.mk + + REQ_VERSION_MAJOR := 2 + REQ_VERSION_PATCH := 6 + MAKE_TARGETS := test01 test02 module01.ko + + include $(top_srcdir)/include/mk/module.mk + include $(top_srcdir)/include/mk/generic_leaf_target.mk + + endif + +This is a Makefile example that allows you to build kernel modules inside LTP. +The prerequisites for the build are detected by the ``configure`` script. + +The ``REQ_VERSION_MAJOR`` and ``REQ_VERSION_PATCH`` describe minimal kernel +version for which the build system tries to build the module. + +The build system is also forward compatible with changes in Linux kernel +internal API so that, if module fails to build, the failure is ignored both on +build and installation. If the userspace counterpart of the test fails to load +the module because the file does not exists, the test is skipped. + +Note the ``ifneq($(KERNELRELEASE),)``. The reason it exists, it is that the +Makefile is executed twice: once by LTP build system and once by kernel kbuild, +see :kernel_doc:`kbuild/modules` in the Linux kernel documentation for details +on external module build. + +Make Rules and Make Variables +----------------------------- + +When using make rules, avoid writing ad hoc rules like: + +.. code-block:: make + + [prog]: [dependencies] + cc -I../../include $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(LDLIBS) \ + -o [prog] [dependencies] + +This makes cross-compilation and determinism difficult, if not impossible. +Besides, implicit rules are your friends and as long as you use ``MAKEOPTS=;`` +in the top-level caller (or do ``$(subst r,$(MAKEOPTS)``) to remove ``-r``), +the compile will complete successfully, assuming all other prerequisites have +been fulfilled (libraries, headers, etc). + +.. list-table:: + :header-rows: 1 + + * - Variable + - Explanation + + * - $(AR) + - The library archiver + + * - $(CC) + - The system C compiler + + * - $(CCP) + - The system C preprocessor + + * - $(CFLAGS) + - C compiler flags + + * - $(CPPFLAGS) + - Preprocessor flags, e.g. ``-I`` arguments + + * - $(DEBUG_CFLAGS) + - Debug flags to pass to ``$(CC)``, ``-g``, etc + + * - $(KVM_LD) + - Special linker for wrapping KVM payload binaries into linkable object + files. Defaults to ``$(LD)``. Change this variable if the KVM Makefile + fails to build files named ``*-payload.o`` + + * - $(LD) + - The system linker (typically ``$(CC)``, but not necessarily) + + * - $(LDFLAGS) + - What to pass in to the linker, including ``-L`` arguments and other ld + arguments, apart from ``-l`` library includes (see ``$(LDLIBS)``). + This should be done in the ``$(CC)`` args passing style when + ``LD := $(CC)``, e.g. ``-Wl,-foo``, as opposed to ``-foo`` + + * - $(LDLIBS) + - Libraries to pass to the linker (e.g. ``-lltp``, etc) + + * - $(LTPLDLIBS) + - LTP internal libraries i.e. these in libs/ directory + + * - $(OPT_CFLAGS) + - Optimization flags to pass into the C compiler, ``-O2``, etc. If you + specify ``-O2`` or higher, you should also specify + ``-fno-strict-aliasing``, because of gcc fstrict-aliasing optimization + bugs in the tree optimizer. Search for **fstrict-aliasing optimization + bug** with your favorite search engine. + + Examples of more recent bugs: tree-optimization/17510 + and tree-optimization/39100. + + Various bugs have occurred in the past due to buggy logic in the + tree-optimization portion of the gcc compiler, from 3.3.x to 4.4. + + * - $(RANLIB) + - What to run after archiving a library + + * - $(WCFLAGS) + - Warning flags to pass to ``$(CC)``, e.g. ``-Werror``, ``-Wall``, etc. + +Make System Variables +--------------------- + +A series of variables are used within the make system that direct what actions +need to be taken. Rather than listing the variables here, please refer to the +comments contained in :master:`include/mk/env_pre.mk`. + +Guidelines and Recommendations +------------------------------ + +Of course, GNU Make manual is the key to understand the Make system, but +following manuals are probably the most important: + +* `Implicit Rules `_ +* `Variables and Expansion `_ +* `Origin Use `_ +* `VPath Use `_ + +.. warning:: + + Rebuild from scratch before committing anything in the build system. diff --git a/doc/developers/debugging.rst b/doc/developers/debugging.rst new file mode 100644 index 00000000..181e5b09 --- /dev/null +++ b/doc/developers/debugging.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Debugging +========= + +This section explains some tricks which can be used to debug test binaries. + +Debug messages +-------------- + +The LTP framework supports ``TDEBUG`` flag test debug messages. These +messages can be enabled using the ``-D`` parameter or setting ``LTP_ENABLE_DEBUG=1`` +environment variable (see :doc:`../users/setup_tests`). + +Tracing and debugging syscalls +------------------------------ + +The new test library runs the actual test (i.e. the ``test()`` function) in a +forked process. To get stack trace of a crashing test in ``gdb`` it's needed to +`set follow-fork-mode child `_. + +To trace the test, please use ``strace -f`` to enable tracing also for the +forked processes. diff --git a/doc/developers/documentation.rst b/doc/developers/documentation.rst new file mode 100644 index 00000000..abb8ac8e --- /dev/null +++ b/doc/developers/documentation.rst @@ -0,0 +1,55 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Documentation +============= + +This section explains how to use and develop the LTP documentation. The current +documentation format is written using +`reStructedText `_ +and it's built on top of `Sphinx `_. + +Building documentation +~~~~~~~~~~~~~~~~~~~~~~ + +Before building, make sure you have python3 ``virtualenv`` module installed. + +.. code-block:: bash + + # run configure to be able to compile doc dependencies in metadata/ + make autotools + ./configure + cd doc + + # prepare virtual environment + python3 -m virtualenv .venv + . .venv/bin/activate + pip install -r requirements.txt + + # build documentation + make + +Once the procedure has been completed, documentation will be visible at +``doc/html/index.html``. + +.. warning:: + + Documentation requires ``Python >= 3.6``. + The current :master:`.readthedocs.yml` workflow is using ``Python 3.12``, + it is tested in GitHub Actions :master:`.github/workflows/ci-sphinx-doc.yml`. + +Validating spelling +~~~~~~~~~~~~~~~~~~~ + +To check documentation words spelling, we provide support for +`aspell `_, so make sure that it's installed. The +documentation can be tested via ``make spelling`` command. Output will be +visible in the ``doc/build`` folder and, if any error will be found, a warning +message will be shown. + +C API documentation +~~~~~~~~~~~~~~~~~~~ + +The C API documentation is generated from headers using +`kernel-doc `_ +syntax which is supported by Sphinx via +`linuxdoc `_ extension. diff --git a/doc/developers/ltp_library.rst b/doc/developers/ltp_library.rst new file mode 100644 index 00000000..f76cbb75 --- /dev/null +++ b/doc/developers/ltp_library.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +LTP Library guidelines +====================== + +General Rules +------------- + +For extending the LTP library API it applies the same general rules as +for :doc:`writing tests <../developers/writing_tests>` +(with strong focus on readability and simplicity), plus: + +#. LTP library tests must go inside :master:`lib/newlib_tests` directory +#. LTP documentation has to be updated according to API changes +#. Do not add new API functions to the old API. Add new functions to + ``tst_.[ch]`` files. + +Shell API +--------- + +API source code is in :master:`testcases/lib/tst_test.sh`, +:master:`testcases/lib/tst_security.sh` and :master:`testcases/lib/tst_net.sh`. + +Changes in the shell API should not introduce uncommon dependencies +(use basic commands installed everywhere by default). + +Shell libraries +~~~~~~~~~~~~~~~ + +Aside from shell API libraries in :master:`testcases/lib` directory, it's +worth putting common code for a group of tests into a shell library. +The filename should end with ``_lib.sh`` and the library should load +``tst_test.sh`` or ``tst_net.sh``. + +Shell libraries should have conditional expansion for ``TST_SETUP`` or +``TST_CLEANUP``, to avoid surprises when test specific setup/cleanup function is +redefined by shell library. + +.. code-block:: bash + + # ipsec_lib.sh + # SPDX-License-Identifier: GPL-2.0-or-later + TST_SETUP="${TST_SETUP:-ipsec_lib_setup}" + ... + . tst_test.sh diff --git a/doc/developers/setup_mailinglist.rst b/doc/developers/setup_mailinglist.rst new file mode 100644 index 00000000..1eee0ae9 --- /dev/null +++ b/doc/developers/setup_mailinglist.rst @@ -0,0 +1,50 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Setting up the Mailing list +=========================== + +Before using ``git send-email``, you need to set up your email client to send +emails from the command line. This typically involves configuring an SMTP server +and authentication details. + +Open a terminal and configure Git with your email settings using the following +commands: + +.. code-block:: bash + + git config --global sendemail.from "Your Name " + git config --global sendemail.smtpserver "smtp.example.com" + git config --global sendemail.smtpuser "your_email@example.com" + git config --global sendemail.smtpserverport 587 + git config --global sendemail.smtpencryption tls + +Replace ``smtp.example.com`` with the SMTP server address provided by your email +provider. Replace ``your_email@example.com`` with your email address. Adjust the +SMTP port and encryption settings according to your email provider's +requirements. + +To test the configuration you can use ``--dry-run`` parameter. + +.. code-block:: bash + + git send-email --dry-run --to "ltp@lists.linux.it" --subject "Test Email" --body "This is a test email." HEAD^ + +Depending on your SMTP server's configuration, you may need to authenticate +before sending emails. If required, configure authentication settings using: + +.. code-block:: bash + + git config --global sendemail.smtpuser "your_email@example.com" + git config --global sendemail.smtppass "your_password" + +Replace ``your_email@example.com`` with your email address and ``your_password`` +with your email account password. + +For any corner case, please take a look at the +`email + git `_ documentation. + +.. note:: + + This method still works in most of the cases, but nowadays we often + require to setup a two factor authentication. If this is the case, please + consider setting up Git accordingly. diff --git a/doc/developers/test_case_tutorial.rst b/doc/developers/test_case_tutorial.rst new file mode 100644 index 00000000..f6495c4d --- /dev/null +++ b/doc/developers/test_case_tutorial.rst @@ -0,0 +1,1036 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Test case tutorial +================== + +This is a step-by-step tutorial on writing a simple C LTP test, where topics +of the LTP and Linux kernel testing will be introduced gradually using a +concrete example. Most sections will include exercises, some trivial and +others not so much. If you find an exercise is leading you off at too much of +a tangent, just leave it for later and move on. + +LTP tests can be written in C or Shell script. This tutorial is **only for tests +written in C** using the new LTP test API. Note that while we go into some +detail on using Git, this is not intended as a canonical or complete guide +for Git. + +Assumptions & Feedback +---------------------- + +We assume the reader is familiar with C, Git and common Unix/Linux/GNU tools +and has some general knowledge of Operating Systems. Experienced Linux +developers may find it too verbose while people new to system level Linux +development may find it overwhelming. + +Comments and feedback are welcome, please direct them to the Mailing list. + +Getting Started +--------------- + +First of all, make sure you have a copy of LTP in the current folder +and we recommended cloning the Linux kernel repository for reference, this +guide will refer to files and directories. + +.. code-block:: bash + + git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + +There are a number of other repositories which are useful for reference as +well, including the `GNU C library (glibc) `_ +and the alternative C library `musl `_. +Some system calls are partially or even entirely implemented in user +land as part of the standard C library. So in these cases, the C library is an +important reference. glibc is the most common C library for Linux, however +musl is generally easier to understand. + +How system calls are implemented varies from one architecture to another and +across kernel and C library versions. To find out whether a system call is +actually accessing the kernel (whether it is actually a system call) on any +given machine you can use the `strace `_ utility. This +intercepts system calls made by an executable and prints them. We will use this +later in the tutorial. + +Choose a System Call to test +---------------------------- + +We will use the ``statx()`` system call, to provide a concrete example of a +test. At the time of writing there is no test for this call which was +introduced in Linux kernel version 4.11. + +Linux system call specific tests are primarily contained in +:master:`testcases/kernel/syscalls`, but you should also :git_man:`grep` the +entire LTP repository to check for any existing usages of a system call. + +One way to find a system call which is not currently tested by the LTP is to +look at :kernel_tree:`include/linux/syscalls.h` in the Linux kernel tree. + +Something the LTP excels to ensure bug-fixes are back ported to +maintenance releases, so targeting a specific regression is another +option. + +Find an untested System call +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Try to find an untested system call which has a manual page (i.e. ``man +syscall`` produces a result). It is a good idea to Git-clone the latest kernel +man-pages repository. + +.. code-block:: bash + + git clone git://git.kernel.org/pub/scm/docs/man-pages/man-pages.git + +At the time of writing, the difference between the latest man-pages release and +the ``HEAD`` of the repository (usually the latest commit) is well over 100 +commits. This represents about 9 weeks of changes. If you are using a stable +Linux distribution, your man-pages package may well be years old. So as with +the kernel, it is best to have the Git repository as a reference. + +You could also find a system call with untested parameters or use whatever it +is you are planning to use the LTP for. + +Create the test skeleton +------------------------ + +I shall call my test ``statx01.c``, by the time you read this that file name +will probably be taken, so increment the number in the file name as +appropriate or replace ``statx`` with the system call in the chosen exercise. + +.. code-block:: bash + + mkdir testcases/kernel/syscalls/statx + cd testcases/kernel/syscalls/statx + echo statx >> .gitignore + +Next open ``statx01.c`` and add the following boilerplate. Make sure to change +the copyright notice to your name/company, correct the test name and minimum +kernel version if necessary. I will explain what the code does below. + +.. code-block:: c + + // SPDX-License-Identifier: GPL-2.0-or-later + /* + * Copyright (c) 2017 Instruction Ignorer <"can't"@be.bothered.com> + */ + + /*\ + * All tests should start with a description of _what_ we are testing. + * Non-trivial explanations of _how_ the code works should also go here. + * Include relevant links, Git commit hashes and CVE numbers. + * Inline comments should be avoided. + */ + + #include "tst_test.h" + + static void run(void) + { + tst_res(TPASS, "Doing hardly anything is easy"); + } + + static struct tst_test test = { + .test_all = run, + .min_kver = "4.11", + }; + +Starting with the ``#include`` statement we copy in the main LTP test library +headers. This includes the most common test API functions and the test harness +initialization code. It is important to note that this is a completely +ordinary, independent C program, however ``main()`` is missing because it is +implemented in :master:`include/tst_test.h`. + +We specify what code we want to run as part of the test using :ref:`struct tst_test`. +Various callbacks can be set by the test writer, including +``test.test_all``, which we have set to ``run()``. The test harness will execute +this callback in a separate process (using ``fork()``), forcibly terminating it +if it does not return after ``test.timeout`` seconds. + +We have also set ``test.min_kver`` to the kernel version where ``statx`` was +introduced. The test library will determine the kernel version at runtime. If +the version is less than 4.11 then the test harness will return ``TCONF``, +indicating that this test is not suitable for the current system +configuration. + +Occasionally features are back ported to older kernel versions, so ``statx`` may +exist on kernels with a lower version. However we don't need to worry about +that unless there is evidence of it happening. + +As mentioned in the code itself, you should specify what you are testing and +the expected outcome, even if it is relatively simple. If your program flow is +necessarily complex and difficult to understand (which is often the case when +trying to manipulate the kernel into doing something bad), then a detailed +explanation of how the code works is welcome. + +What you should not do, is use inline comments or include the same level of +explanation which is written here. As a general rule, if something is easy to +document, then the code should also be easy to read. So don't document the easy +stuff (except for the basic test specification). + +Before continuing we should compile this and check that the basics work. In +order to compile the test we need a ``Makefile`` in the same subdirectory. If +one already exists, then nothing needs to be done, otherwise add one with the +following contents. + +.. code-block:: make + + # SPDX-License-Identifier: GPL-2.0-or-later + # Copyright (c) 2019 Linux Test Project + + top_srcdir ?= ../../../.. + + include $(top_srcdir)/include/mk/testcases.mk + + include $(top_srcdir)/include/mk/generic_leaf_target.mk + +This will automatically add ``statx01.c`` as a build target producing a +``statx01`` executable. Unless you have heavily deviated from the tutorial, and +probably need to change ``top_srcdir``, nothing else needs to be done. + +Normally, if you were starting a Makefile from scratch, then you would need to +add ``statx01`` as a build target. Specifying that you would like to run some +program (e.g. ``gcc`` or ``clang``) to transform ``statx01.c`` into ``statx01``. +Here we don't need to do that, but sometimes it is still necessary. For example, +if we needed to link to the POSIX threading library, then we could add the +following line after ``testcases.mk``. + +.. code-block:: make + + statx01: CFLAGS += -pthread + +Assuming you are in the test's subdirectory :master:`testcases/kernel/syscalls/statx`, +please do: + +.. code-block:: bash + + make + ./statx01 + +This should build the test and then run it. However, even though the test is +in :master:`testcases/kernel/syscalls` directory it won't be automatically ran +as part of the syscalls test group (e.g. not run via ``kirk -r math`` or +``./runltp -f syscalls``). For this we need to add it to the runtest file. So +open :master:`runtest/syscalls` and add the lines starting with a ``+``. + +.. code-block:: + + statvfs01 statvfs01 + statvfs02 statvfs02 + + +statx01 statx01 + + + stime01 stime01 + stime02 stime02 + +The :master:`runtest` files are in a two column format. The first column is the +test name, which is mainly used by test runners for reporting and filtering. It +is just a single string of text with no spaces. The second column, which can +contain spaces, is passed to the shell in order to execute the test. Often it +is just the executable name, but some tests also take arguments (the LTP has a +library for argument parsing, by the way). + +If you haven't done so already, we should add all these new files to Git. It +is vitally important that you do not make changes to the master branch. If you +do then pulling changes from upstream becomes a major issue. So first of all +create a new branch. + +.. code-block:: bash + + git checkout -b statx01 master + +Now we want to add the files we have created or modified, but before doing a +commit make sure you have configured Git correctly. You need to at least set +your Name and e-mail address in ``~/.gitconfig``, but there are some other +settings which come in handy too. My relatively simple configuration is similar +to the below: + +.. code-block:: ini + + [user] + name = Sarah Jane + email = sjane@e-mail.address + [core] + editor = emacs + [sendemail] + smtpServer = smtp.server.address + +Obviously you need to at least change your name and e-mail. The SMTP server is +useful for :git_man:`send-email`, which we will discuss later. The editor value is +used for things like writing commits (without the ``-m`` option). + +.. code-block:: bash + + git add -v :/testcases/kernel/syscalls/statx :/runtest/syscalls + git commit -m "statx01: Add new test for statx syscall" + +This should add all the new files in the ``statx`` directory and the ``runtest`` +file. It is good practice to commit early and often. Later on we will do a +Git-rebase, which allows us to clean up the commit history. So don't worry +about how presentable your commit log is for now. Also don't hesitate to +create a new branch when doing the exercises or experimenting. This will allow +you to diverge from the tutorial and then easily come back again. + +I can't emphasize enough that Git makes things easy through branching and that +things quickly get complicated if you don't do it. However if you do get into +a mess, Git-reflog and Git-reset, will usually get you out of it. If you also +mess that up then it may be possible to cherry pick 'dangling' commits out of +the database into a branch. + +Report TCONF instead of TPASS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Maybe the test should report ``TCONF: Not implemented`` instead or perhaps +``TBROK``. Try changing it do so. + +Check Git ignores the executable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Is your ``.gitignore`` correct? + +Run make check +~~~~~~~~~~~~~~~~~~ + +Check coding style with ``make check``. + +Install the LTP and run the test with runtest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Run ``statx01`` on its own, also using ``-I0`` amd ``-I10``. + +Call the system call +-------------------- + +At the time of writing ``statx`` has no ``glibc`` wrapper. It is also fairly common +for a distribution's C library version to be older than its kernel or it may use a +cut down C library in comparison to the GNU one. So we must call ``statx()`` +using the general ``syscall()`` interface. + +LTP contains a library for dealing with the ``syscall`` interface, which is +located in :master:`include/lapi`. System call numbers are listed against the relevant +call in the ``*.in`` files (e.g. ``x86_64.in``) which are used to generate +``syscalls.h``, the header you should include. +System call numbers vary between architectures, hence there are multiple +``*.in`` files for each architecture. + +On rare occasions, you may find that system call number is missing from ``*.in`` +files. In these cases, they will need to be updated using +:master:`include/lapi/syscalls/generate_arch.sh` script as following: + +.. code-block:: bash + + $ include/lapi/syscalls/generate_arch.sh /path/to/Linux/sources + +The script will generate all the needed ``*.in`` files accordingly to the Linux +source code which has been used. Make sure that your Linux source code has +been updated to the latest version. + +Once the new syscalls files have been updated, to rebuild our ``syscalls.h`` +file, please re-run ``./configure`` script. + +.. code-block:: c + + /* + * Test statx + * + * Check if statx exists and what error code it returns when we give it dodgy + * data. + */ + + #include + #include "tst_test.h" + #include "lapi/syscalls.h" + + struct statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t __reserved; + }; + + struct statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t __spare0[1]; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct statx_timestamp stx_atime; + struct statx_timestamp stx_btime; + struct statx_timestamp stx_ctime; + struct statx_timestamp stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t __spare2[14]; + }; + + static int sys_statx(int dirfd, const char *pathname, int flags, + unsigned int mask, struct statx *statxbuf) + { + return tst_syscall(__NR_statx, dirfd, pathname, flags, mask, statxbuf); + } + + ... + +So the top part of the code is now boiler plate for calling ``statx``. It is +common for the kernel to be newer than the user land libraries and headers. So +for new system calls like ``statx``, we copy, with a few modifications, the +relevant definitions into the LTP. This is somewhat like 'vendoring', although +we are usually just copying headers required for interacting with the Kernel's +ABI (Application Binary Interface), rather than integrating actual +functionality. + +So from the top we include the ``stdint.h`` library which gives us the standard +``(u)int*_t`` type definitions. We use these in place of the Kernel type +definitions such as ``__u64`` in ``linux/types.h``. We then have a couple of +structure definitions which form part of the ``statx`` API. These were copied +from :kernel_tree:`include/uapi/linux/stat.h` in the Linux kernel tree. + +After that, there is a wrapper function, which saves us from writing +``tst_syscall(__NR_statx, ...``, every time we want to make a call to +``statx``. This also provides a stub for when ``statx`` is eventually integrated +into the LTP library and also implemented by the C library. At that point we +can switch to using the C library implementation if available or fallback to +our own. + +The advantage of using the C library implementation is that it will often be +better supported across multiple architectures. It will also mean we are using +the system call in the same way most real programs would. Sometimes there are +advantages to bypassing the C library, but in general it should not be our +first choice. + +The final test should do a check during configuration (i.e. when we run +``./configure`` before building) which checks if the ``statx`` system call and +associated structures exists. This requires writing an ``m4`` file for use with +:master:`configure.ac` which is processed during ``make autotools`` and produces the +configure script. + +For the time being though we shall just ignore this. All you need to know for +now is that this is a problem which eventually needs to be dealt with and that +there is a system in place to handle it. + +.. code-block:: c + + ... + + static void run(void) + { + struct statx statxbuf = { 0 }; + + TEST(sys_statx(0, NULL, 0, 0, &statxbuf)); + + if (TST_RET == 0) + tst_res(TFAIL, "statx thinks it can stat NULL"); + else if (TST_ERR == EFAULT) + tst_res(TPASS, "statx set errno to EFAULT as expected"); + else + tst_res(TFAIL | TERRNO, "statx set errno to some unexpected value"); + } + + static struct tst_test test = { + .test_all = run, + .min_kver = "4.11", + }; + +The ``TEST`` macro sets ``TST_RET`` to the return value of ``tst_statx()`` and +``TST_ERR`` to the value of ``errno`` immediately after the functions +return. This is mainly just for convenience, although it potentially could +have other uses. + +We check whether the return value indicates success and if it doesn't also +check the value of ``errno``. The last call to ``tst_res`` includes ``TERRNO``, +which will print the current error number and associated description in +addition to the message we have provided. Note that it uses the current value +of ``errno`` not ``TST_ERR``. + +What we should have done in the example above is use ``TTERRNO`` which takes the +value of ``TST_ERR``. + +If we try to run the test on a kernel where ``statx`` does not exist, then +``tst_syscall`` will cause it to fail gracefully with ``TCONF``. Where ``TCONF`` +indicates the test is not applicable to our configuration. + +The function ``tst_syscall`` calls ``tst_brk(TCONF,...)`` on failure. ``tst_brk`` +causes the test to exit immediately, which prevents any further test code from +being run. + +What are the differences between ``tst_brk`` and ``tst_res``? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See :master:`include/tst_test.h`. Also what do they have in common? + +What happens if you call ``tst_res(TINFO, ...)`` after ``sys_statx``? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Does the test still function correctly? + +Extend the test to handle other basic error conditions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example, see if you can trigger ``ENOENT`` instead. You shouldn't +have to create any files, which is discussed in the next section. + +Setup, Cleanup and files +------------------------ + +Some tests require resources to be allocated, or system settings to be +changed, before the test begins. This ``setup`` only has to be done once at the +beginning and at the end of the test needs to be removed or reverted. The +``cleanup`` also has to be done regardless of whether the test breaks. + +Fortunately, like most test libraries, we have setup and cleanup (teardown) +callbacks. ``setup`` is called once before ``run`` and ``cleanup`` is called once +afterwards. Note that ``run`` itself can be called multiple times by the test +harness, but that ``setup`` and ``cleanup`` are only called once. + +If either your code, a ``SAFE_*`` macro or a library function such as +``tst_syscall`` call ``tst_brk``, then ``run`` will exit immediately and the +``cleanup`` function is then called. Once ``cleanup`` is completed, the test +executable will then exit altogether abandoning any remaining iterations of +``run``. + +For ``statx`` we would like to create some files or file like objects which we +have control over. Deciding where to create the files is easy, we just create +it in the current working directory and let the LTP test harness handle where +that should be by setting ``.needs_tmpdir = 1``. + +.. code-block:: c + + /* + * Test statx + * + * Check if statx exists and what error code it returns when we give it dodgy + * data. Then stat a file and check it returns success. + */ + + #include + #include "tst_test.h" + #include "lapi/syscalls.h" + #include "lapi/fcntl.h" + + #define FNAME "file_to_stat" + #define STATX_BASIC_STATS 0x000007ffU + + /*************** statx structure and wrapper goes here ! ***************/ + ... + +We have added an extra include :master:`lapi/fcntl.h` which wraps the system header by +the same name (``#include ``). This header ensures we have definitions +for recently added macros such as ``AT_FDCWD`` by providing fall backs if the +system header does not have them. The :master:`lapi/` directory contains a number of +headers like this. + +At some point we may wish to add :master:`lapi/stat.h` to provide a fall back for +macros such as ``STATX_BASIC_STATS``. However for the time being we have just +defined it in the test. + + +.. code-block:: c + + ... + + static void setup(void) + { + SAFE_TOUCH(FNAME, 0777, NULL); + } + + static void run(void) + { + struct statx statxbuf = { 0 }; + + TEST(sys_statx(0, NULL, 0, 0, &statxbuf)); + if (TST_RET == 0) + tst_res(TFAIL, "statx thinks it can stat NULL"); + else if (TST_ERR == EFAULT) + tst_res(TPASS, "statx set errno to EFAULT as expected"); + else + tst_res(TFAIL | TERRNO, "statx set errno to some unexpected value"); + + TEST(sys_statx(AT_FDCWD, FNAME, 0, STATX_BASIC_STATS, &statxbuf)); + if (TST_RET == 0) + tst_res(TPASS, "It returned zero so it must have worked!"); + else + tst_res(TFAIL | TERRNO, "statx can not stat a basic file"); + } + + static struct tst_test test = { + .setup = setup, + .test_all = run, + .min_kver = "4.11", + .needs_tmpdir = 1 + }; + +The ``setup`` callback uses one of the LTP's ``SAFE`` functions to create an empty +file ``file_to_stat``. Because we have set ``.needs_tmpdir``, we can just create +this file in the present working directory. We don't need to create a +``cleanup`` callback yet because the LTP test harness will recursively delete +the temporary directory and its contents. + +The ``run`` function can be called multiple times by the test harness, however +``setup`` and ``cleanup`` callbacks will only be ran once. + +.. warning:: + + By this point you may have begun to explore the LTP library headers or older + tests. In which case you will have come across functions from the old API such + as ``tst_brkm``. The old API is being phased out, so you should not use these + functions. + +So far we haven't had to do any clean up. So our example doesn't answer the +question "what happens if part of the clean up fails?". To answer this we are +going to modify the test to ask the (highly contrived) question "What happens +if I create and open a file, then create a hard-link to it, then call open +again on the hard-link, then ``stat`` the file". + + +.. code-block:: c + + #define LNAME "file_to_stat_link" + + ... + + static void setup(void) + { + fd = SAFE_OPEN(FNAME, O_CREAT, 0777); + SAFE_LINK(FNAME, LNAME); + lfd = SAFE_OPEN(LNAME, 0); + } + + static void cleanup(void) + { + if (lfd != 0) + SAFE_CLOSE(lfd); + + if (fd != 0) + SAFE_CLOSE(fd); + } + + static void run(void) + { + ... + + TEST(sys_statx(AT_FDCWD, LNAME, 0, STATX_BASIC_STATS, &statxbuf)); + if (TST_RET == 0) + tst_res(TPASS, "It returned zero so it must have worked!"); + else + tst_res(TFAIL | TERRNO, "statx can not stat a basic file"); + } + + static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .tcnt = 2, + .min_kver = "4.11", + .needs_tmpdir = 1 + }; + +Because we are now opening a file, we need a ``cleanup`` function to close the +file descriptors. We have to manually close the files to ensure the temporary +directory is deleted by the test harness (see :doc:`writing_tests` for details). + +As a matter of good practice, the file descriptors are closed in reverse +order. In some circumstances the order which ``cleanup`` is performed is +significant. In those cases, resources created towards the end of ``setup`` are +dependent to ones near the beginning. During ``cleanup`` we remove the +dependants before their dependencies. + +If, for some reason, the file descriptor ``lfd`` became invalid during the test, +but ``fd`` was still open, we do not want ``SAFE_CLOSE(lfd)`` to cause the +``cleanup`` function to exit prematurely. If it did, then ``fd`` would remain +open which would cause problems on some file systems. + +Nor do we want to call ``cleanup`` recursively. So during ``cleanup`` +``tst_brk``, and consequently the ``SAFE`` functions, do not cause the test to +exit with ``TBROK``. Instead they just print an error message with ``TWARN``. + +It is not entirely necessary to check if the file descriptors have a none zero +value before attempting to close them. However it avoids a bunch of spurious +warning messages if we fail to open ``file_to_stat``. Test case failures can be +difficult to interpret at the best of times, so avoid filling the log with +noise. + +Check ``statx`` returns the correct number of hard links +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The field ``statx.stx_nlink`` should be equal to 2, right? + +Git-branch +~~~~~~~~~~ + +We are about to make some organizational changes to the test, so now would be +a good time to branch. Then we can switch between the old and new versions, to +check the behavior has not been changed by accident. + +Split the test +-------------- + +In our current test, we have essentially rolled two different test cases into +one. Firstly we check if an error is returned when bad arguments are provided +and secondly we check what happens when we stat an actual file. Quite often it +makes sense to call ``tst_res`` multiple times in a single test case because we +are checking different properties of the same result, but here we are clearly +testing two different scenarios. + +So we should split the test in two. One obvious way to do this is to create +``statx02.c``, but that seems like overkill in order to separate two simple test +cases. So, for now at least, we are going to do it a different way. + +.. code-block:: c + + ... + + static void run_stat_null(void) + { + struct statx statxbuf = { 0 }; + + TEST(sys_statx(0, NULL, 0, 0, &statxbuf)); + if (TST_RET == 0) + tst_res(TFAIL, "statx thinks it can stat NULL"); + else if (TST_ERR == EFAULT) + tst_res(TPASS, "statx set errno to EFAULT as expected"); + else + tst_res(TFAIL | TERRNO, "statx set errno to some unexpected value"); + } + + static void run_stat_symlink(void) + { + struct statx statxbuf = { 0 }; + + TEST(sys_statx(AT_FDCWD, LNAME, 0, STATX_BASIC_STATS, &statxbuf)); + if (TST_RET == 0) + tst_res(TPASS, "It returned zero so it must have worked!"); + else + tst_res(TFAIL | TERRNO, "statx can not stat a basic file"); + } + + static void run(unsigned int i) + { + switch(i) { + case 0: run_stat_null(); + case 1: run_stat_symlink(); + } + } + + static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test = run, + .tcnt = 2, + .min_kver = "4.11", + .needs_tmpdir = 1 + }; + +So we have used an alternative form of the ``test`` or ``run`` callback which +accepts an index. Some tests use this index with an array of parameters and +expected return values. Others do something similar to the above. The index +can be used how you want so long as each iteration calls ``tst_res`` in a +meaningful way. + +If an iteration fails to return a result (i.e. call ``tst_res`` with a value +other than ``TINFO``) then the test harness will report ``TBROK`` and print the +iteration which failed. This prevents a scenario in your test from silently +failing due to some faulty logic. + +What is wrong with the switch statement? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Were you paying attention? Also see the output of ``make check``. + +Test a feature unique to statx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +So far we have not tested anything which is unique to ``statx``. So, for +example, you could check stx_btime is correct (possibly only to within a +margin of error) and that it differs from ``stx_mtime`` after writing to the +file. + +Alternatively you could check that ``stx_dev_major`` and ``stx_dev_minor`` are set +correctly. Note that the LTP has helper functions for creating devices and +file systems. + +This could be quite a challenging exercise. You may wish to tackle an +altogether different test scenario instead. If you get stuck just move onto +the next section and come back later. + +Submitting the test for review +------------------------------ + +Ignoring the fact we should probably create :master:`lapi/stat.h` along with a bunch +of fallback logic in the build system. We can now get our test ready for +submission. + +The first thing you need to do before considering submitting your test is run +``make check-statx01`` or ``make check`` in the test's directory. Again, we use +the kernel style guidelines where possible. Next you should create a new +branch, this will allow you to reshape your commit history without fear. + +After that we have the pleasure of doing an interactive ``rebase`` to clean up +our commit history. In its current form the test only really needs a single +commit, but if you have been using Git correctly then you should have +many. The main reason we want to compress it to a single commit, is to make +the LTP's Git-log readable. It also allows us to write a coherent description +of the work as a whole in retrospective. Although, when adding a new test, the +test description in the code will probably make the commit message redundant. + +Anyway, as an example, we shall look at my personal commit history from this +tutorial and ``rebase`` it. You should try following along with your own +repository. First lets look at the commit history since we branched from +master. + +.. code-block:: bash + + git log --oneline master..HEAD + 152d39fe7 (HEAD -> tutorial-rebase2, tutorial-rebase) tutorial: Start Submitting patch section + 70f7ce7ce statx01: Stop checkpatch from complaining + bb0332bd7 tutorial: Fix review problems + 6a87a084a statx01: Fix review problems + d784b1e85 test-writing-guidelines: Remove old API argument + c26e1be7a fixup! tutorial + 1e24a5fb5 (me/tutorial-rebase) fixup! tutorial + 568a3f7be fixup! tutorial + 09dd2c829 statx: stage 6 + bfeef7902 statx: stage 5b + 76e03d714 statx: stage 5a + 98f5bc7ac statx: stage 4 + 6f8c16438 statx: stage 3 (Add statx01) + 5d93b84d8 Add statx and other syscall numbers + 5ca627b78 tutorial: Add a step-by-step C test tutorial + +So we have told git to show all the commits which don't exist in ``master``, but +are in ``HEAD``, where ``HEAD`` is the top of the current branch. The current +branch is ``tutorial-rebase2`` which I just created. I have already done one +``rebase`` and submitted a patch for review, so my original branch was just called +``tutorial``. + +As usual my commit history is starting to look like a bit of mess! There is +even a commit in there which should not be in the this branch (Remove old API +argument), however it can be ignored for now and 'cherry picked' into a new branch +later. + +For my patch I actually need at least two commits, one which contains the +tutorial text and one which contains the test and associated files. So first +of all I want to 'squash' (amalgamate) all the commits appended with +``tutorial:`` into the bottom commit. + +.. code-block:: bash + + $ git rebase -i 5ca627b78\^ + ... + +This begins an interactive ``rebase`` where commit ``5ca6427b78`` is the earliest +commit we want to edit. The ``^`` symbol after the commit hash, specifies the +commit before this one. The interactive ``rebase`` command takes the last commit +we want to keep unaltered as it's argument (in other words it takes a +non-inclusive range). + +Upon entering a similar command you will be presented with a text file +similar to the following. The file should be displayed in your text editor of +choice, if it doesn't, then you may change the editor variable in +``.gitconfig``. + +.. code-block:: bash + + pick 5ca627b78 tutorial: Add a step-by-step C test tutorial + pick 5d93b84d8 Add statx and other syscall numbers + pick 6f8c16438 statx: stage 3 (Add statx01) + pick 98f5bc7ac statx: stage 4 + pick 76e03d714 statx: stage 5a + pick bfeef7902 statx: stage 5b + pick 09dd2c829 statx: stage 6 + pick 568a3f7be fixup! tutorial + pick 1e24a5fb5 fixup! tutorial + pick c26e1be7a fixup! tutorial + pick d784b1e85 test-writing-guidelines: Remove old API argument + pick 6a87a084a statx01: Fix review problems + pick bb0332bd7 tutorial: Fix review problems + pick 70f7ce7ce statx01: Stop checkpatch from complaining + pick 152d39fe7 tutorial: Start Submitting patch section + +The last commit from Git-log is shown at the top. The left hand column +contains the commands we want to run on each commit. ``pick`` just means we +re-apply the commit as-is. We can reorder the lines to apply the commits in a +different order, but we need to be careful when reordering commits to the same +file. If your ``rebase`` results in a merge conflict, then you have probably +reordered some commits which contained changes to the same piece of code. + +Perhaps a better name for the interactive ``rebase`` command would be 'replay'. As +we pick a point in the commit history, undo all those commits before that +point, then reapply them one at a time. During the replay we can reorder the +commits, drop, merge, split and edit them, creating a new history. + +The commands I am going to use are ``reword`` and ``fixup``. The ``reword`` command +allows you to edit a single commit's message. The 'fixup' command 'squashes' a +commit into the commit above/preceding it, merging the two commits into +one. The commit which has ``fixup`` applied has its commit message deleted. If +you think a commit might have something useful in its message then you can use +``squash`` instead. + +.. code-block:: bash + + reword 5ca627b78 tutorial: Add a step-by-step C test tutorial + fixup 568a3f7be fixup! tutorial + fixup 1e24a5fb5 fixup! tutorial + fixup c26e1be7a fixup! tutorial + fixup bb0332bd7 tutorial: Fix review problems + fixup 152d39fe7 tutorial: Start Submitting patch section + fixup 276edecab tutorial: Save changes before rebase + pick 5d93b84d8 Add statx and other syscall numbers + pick 6f8c16438 statx: stage 3 (Add statx01) + pick 98f5bc7ac statx: stage 4 + pick 76e03d714 statx: stage 5a + pick bfeef7902 statx: stage 5b + pick 09dd2c829 statx: stage 6 + pick d784b1e85 test-writing-guidelines: Remove old API argument + pick 6a87a084a statx01: Fix review problems + +So all the commits marked with ``fixup`` will be re-played by Git immediately +after 5ca62 at the top. A new commit will then be created with the amalgamated +changes of all the commits and 5ca62's log message. It turns out that I didn't +need to reword anything, but there is no harm in checking. It is easy to +forget the ``Signed-off-by:`` line. + +I could now do the same for the commits to the ``statx`` test, making the commit +message prefixes consistent. However I am not actually going to submit the +test (yet). + +I won't attempt to show you this, but if you need to do the opposite and split +apart a commit. It is also possible using Git-rebase by marking a line with +``edit``. This will pause Git just after replaying the marked commit. You can +then use a 'soft' Git-reset to bring the selected commit's changes back into +the 'index' where you are then able to un-stage some parts before +re-committing. + +You can also use ``edit`` and ``git commit --amend`` together to change a commit +deep in your history, but without resetting the 'index'. The 'index' contains +changes which you have staged with :git_man:`add`, but not yet committed. + +So now that the commit history has been cleaned up, we need to submit a patch +to the mailing list or make a pull request on GitHub. The mailing list is the +preferred place to make submissions and is more difficult for most people, so +I will only cover that method. + +Just before we create the patch, we need to check that our changes will still +apply to the master branch without problems. To do this we can use another +type of ``rebase`` and then try rebuilding and running the test. + +.. code-block:: bash + + git checkout master + git pull origin + git checkout tutorial-rebase2 + git rebase master + +Above, I update the master branch and then replay our changes onto it using +``git rebase master``. You may find that after the rebase there is a merge +conflict. This will result in something which looks like the following (taken +from a Makefile conflict which was caused by reordering commits in a ``rebase``). + +.. code-block:: diff + + <<<<<<< HEAD + cve-2016-7117: LDFLAGS += -lpthread + ======= + cve-2014-0196: LDFLAGS += -lpthread -lutil -lrt + cve-2016-7117: LDFLAGS += -lpthread -lrt + >>>>>>> 4dbfb8e79... Add -lrt + +The first line tells us this is the beginning of a conflict. The third line +separates the two conflicting pieces of content and the last line is the end +of the conflict. Usually, all you need to do is remove the lines you don't +want, stage the changes and continue the ``rebase`` with ``git rebase +--continue``. + +In order to create a patch e-mail we use :git_man:`format-patch`, +we can then send that e-mail using :git_man:`send-email`. +It is also possible to import the patch (``mbox``) file into a number of e-mail +programs. + +.. code-block:: bash + + $ git format-patch -1 -v 2 -o output --to ltp@lists.linux.it fd3cc8596 + output/v2-0001-tutorial-Add-a-step-by-step-C-test-tutorial.patch + +The first argument ``-1`` specifies we want one commit from fd3cc8596 +onwards. If we wanted this commit and the one after it we could specify ``-2`` +instead. + +This is my second patch submission so I have used ``-v 2``, which indicates this +is the second version of a patch set. The ``-o`` option specifies the output +directory (literally called ``output``). The ``--to`` option adds the ``To:`` e-mail +header, which I have set to the LTP mailing list. + +We can then send this patch with the following command sans ``--dry-run``. + +.. code-block:: bash + + git send-email --dry-run output/v2-0001-tutorial-Add-a-step-by-step-C-test-tutorial.patch + +Git will ask some questions (which you can ignore) and then tell you what it +would do if this weren't a dry-run. In order for this to work you have to have +a valid SMTP server set in ``.gitconfig`` and also be signed up to the LTP +mailing list under the same e-mail address you have configured in Git. You can +sign up at https://lists.linux.it/listinfo/ltp. + +Doing code review +----------------- + +While waiting for your test to be reviewed, you are invited and encouraged to +review other contributors' code. This may seem bizarre when you are completely +new to the project, but there are two important ways in which you can +contribute here: + +A. Point out logical errors in the code. +B. Improve your own understanding + +It doesn't matter whether you know the canonical way of writing an LTP test in +C. An error of logic, when properly explained, is usually indisputable. These +are the most important errors to find as they always result in false test +results. Once someone points out such an error it is usually obvious to +everyone that it is a bug and needs to be fixed. + +Obviously testing the patch is one way of finding errors. You can apply patches +using :git_man:`am`. Then it is just a case of compiling and running the tests. + +Finally, reading and attempting to comment on other peoples patches, gives +you a better understanding of the reviewers perspective. This is better for +the project and for you. + +Style and organizational issues are best left to after you have found logical +errors. + +Final notes +----------- + +Hopefully you can now grasp the structure of an LTP test and have some idea of +what is available in the LTP test library. There are a vast number of library +functions available (mainly located in include and lib), some of which are +documented in the test writing guidelines and many of which are not. + +We have only scratched the surface of the immense technical complexity of +systems programming across multiple Kernel and C lib versions as well as +different hardware architectures. The important thing to take away from this +is that you have to be conscientious of what will happen on systems different +from yours. The LTP has a huge and varied user base, so situations you may +think are unlikely can and do happen to somebody. + +Of course you don't want to spend time allowing for situations which may never +arise either, so you have to do your research and think about each situation +critically. The more systems you can test on before submitting your changes, +the better, although we understand not everyone has access to a lab. + +One important topic which has not been covered by this tutorial, is +multi-process or multi-threaded testing. The LTP library functions work inside +child processes and threads, but their semantics change slightly. There are +also various helper functions for synchronizing and forking processes. + +.. note:: + + When it comes time to submit a test, the preferred way to do it is on the + mailing list although you can also use GitHub. The LTP follows similar rules + to the kernel for formatting and submitting patches. Generally speaking the + review cycle is easier for small patches, so try to make small changes or + additions where possible. diff --git a/doc/developers/writing_tests.rst b/doc/developers/writing_tests.rst new file mode 100644 index 00000000..24ea2654 --- /dev/null +++ b/doc/developers/writing_tests.rst @@ -0,0 +1,537 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Writing tests +============= + +This document describes LTP guidelines and it's intended for anybody who wants +to write or to modify a LTP testcase. It's not a definitive guide and it's not, +by any means, a substitute for common sense. + +Guide to clean and understandable code +-------------------------------------- + +Testcases require that the source code is easy to follow. When a test starts to +fail, the failure has to be analyzed and clean test codebase makes this task +much easier and quicker. + +Keep things simple +~~~~~~~~~~~~~~~~~~ + +It's worth to keep testcases simple or, better, as simple as possible. + +The kernel and libc are tricky beasts and the complexity imposed by their +interfaces is quite high. Concentrate on the interface you want to test and +follow the UNIX philosophy. + +It's a good idea to make the test as self-contained as possible too, ideally +tests should not depend on tools or libraries that are not widely available. + +Do not reinvent the wheel! + +* Use LTP standard interface +* Do not add custom PASS/FAIL reporting functions +* Do not write Makefiles from scratch, use LTP build system instead +* Etc. + +Keep functions and variable names short +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choosing a good name for an API functions or even variables is a difficult +task do not underestimate it. + +There are a couple of customary names for different things that help people to +understand code, for example: + +* For loop variables are usually named with a single letter ``i``, ``j``, ... +* File descriptors ``fd`` or ``fd_foo``. +* Number of bytes stored in file are usually named as ``size`` or ``len`` +* Etc. + +Do not over-comment +~~~~~~~~~~~~~~~~~~~ + +Comments can sometimes save your day, but they can easily do more harm than +good. There has been several cases where comments and actual implementation +drifted slowly apart which yielded into API misuses and hard to find bugs. +Remember there is only one thing worse than no documentation: wrong +documentation. + +Ideally, everybody should write code that is obvious, which unfortunately isn't +always possible. If there is a code that requires to be commented, keep it +short and to the point. These comments should explain *why* and not *how* +things are done. + +Never ever comment the obvious. + +In case of LTP testcases, it's customary to add an `RST +`_ +formatted comment paragraph with high-level test description at the beginning +of the file right under the GPL SPDX header. This helps other people to +understand the overall goal of the test before they dive into the technical +details. It's also exported into generated documentation hence it should mostly +explain what is tested. + +DRY (Code duplication) +~~~~~~~~~~~~~~~~~~~~~~ + +Copy & paste is a good servant but very poor master. If you are about to copy a +large part of the code from one testcase to another, think what would happen if +you find bug in the code that has been copied all around the tree. What about +moving it to a library instead? + +The same goes for short but complicated parts, whenever you are about to copy & +paste a syscall wrapper that packs arguments accordingly to machine +architecture or similarly complicated code, put it into a header instead. + +C coding style +-------------- + +LTP adopted `Linux kernel coding style `_. +Run ``make check`` in the test's directory and/or use ``make check-$TCID``, it +uses (among other checks) our vendoring version of +`checkpatch.pl `_ +script from kernel git tree. + +.. note:: + If ``make check`` does not report any problems, the code still may be wrong + as all tools used for checking only look for common mistakes. + +The following linting code can be found when we run ``make check``: + +.. list-table:: + :header-rows: 1 + + * - Linting code + - Message + - Explanation + + * - LTP-001 + - Library source files have ``tst_`` prefix + - API source code is inside headers in ``include/{empty}*.h``, + ``include/lapi/{empty}*.h`` (backward compatibility for old kernel and + libc) and C sources in ``lib/{empty}*.c``. Files must have ``tst_`` + prefix. + + * - LTP-002 + - ``TST_RET`` and ``TST_ERR`` are never modified by test library functions + - The test author is guaranteed that the test API will not modify these + variables. This prevents silent errors where the return value and + errno are overwritten before the test has chance to check them. + + The macros which are clearly intended to update these variables. That + is ``TEST`` and those in :master:`include/tst_test_macros.h`. Are of + course allowed to update these variables. + + * - LTP-003 + - Externally visible library symbols have the ``tst_`` prefix + - Functions, types and variables in the public test API should have the + ``tst_`` prefix. With some exceptions for symbols already prefixed with + ``safe_`` or ``ltp_``. + + Static (private) symbols should not have the prefix. + + * - LTP-004 + - Test executable symbols are marked ``static`` + - Test executables should not export symbols unnecessarily. This means + that all top-level variables and functions should be marked with the + ``static`` keyword. The only visible symbols should be those included + from shared object files. + + * - LTP-005 + - Array must terminate with a sentinel value (i.e. ``NULL`` or ``{}``) + - When defining arrays in the :ref:`struct tst_test` structure, we need to + end the array items with a sentinel ``NULL`` value. + +Shell coding style +------------------ + +When writing testcases in shell, write in *portable shell* only, it's a good +idea to try to run the test using alternative shell (alternative to bash, for +example dash) too. + +*Portable shell* means Shell Command Language as defined by POSIX with an +exception of few widely used extensions, namely **local** keyword used inside of +functions and ``-o`` and ``-a`` test parameters (that are marked as obsolete in +POSIX). + +You can either try to run the testcases in Debian which has ``/bin/sh`` pointing +to ``dash`` by default, or to install ``dash`` on your favorite distribution, +then use it to run tests. If your distribution lacks ``dash`` package, you can +always compile it from `sources `_. + +Run ``make check`` in the test's directory and/or use ``make check-$TCID.sh``. +It uses (among other checks) our vendoring version of +`checkbashism.pl `_ +from Debian that is used to check for non-portable shell code. + +.. note:: + + If ``make check`` does not report any problems the code still may be wrong, + as ``checkbashisms.pl`` is used for checking only common mistakes. + +Here there are some common sense style rules for shell + +* Keep lines under 80 chars +* Use tabs for indentation +* Keep things simple, avoid unnecessary subshells +* Don't do confusing things (i.e. don't name your functions like common shell + commands, etc.) +* Quote variables +* Be consistent + +3 Backwards compatibility +~~~~~~~~~~~~~~~~~~~~~~~~~ + +LTP test should be as backward compatible as possible. Think of an enterprise +distributions with long term support (more than five years since the initial +release) or of an embedded platform that needs to use several years old +toolchain supplied by the manufacturer. + +Therefore LTP test for more current features should be able to cope with older +systems. It should at least compile fine and if it's not appropriate for the +configuration it should return ``TCONF``. + +There are several types of checks we use: + +* The *configure script* is usually used to detect availability of a function + declarations in system headers. It's used to disable tests at compile time or + to enable fallback definitions. + +* Checking the ``errno`` value is another type of runtime check. Most of the + syscalls returns either ``EINVAL`` or ``ENOSYS`` when syscall was not + implemented or was disabled upon kernel compilation. + +* LTP has kernel version detection that can be used to disable tests at runtime. + Unfortunately, the kernel version does not always corresponds to a well + defined feature set, as distributions tend to backport hundreds of patches + while the kernel version stays the same. Use with caution. + +* Lately, we added a kernel ``.config`` parser. A test can define a boolean + expression of kernel config variables that has to be satisfied in order to run + a test. At the moment, this is mostly used for kernel namespaces. + +* Sometimes it makes sense to define a few macros instead of creating a + configure test. One example is Linux specific POSIX clock ids in + :master:`include/lapi/posix_clocks.h`. + +Dealing with messed up legacy code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +LTP still contains a lot of old and messy code and we are cleaning it up as +fast as we can but, despite the decade of efforts, there is still a lot of it. +If you start modifying old or a messy testcase and your changes are more +complicated than simple typo fixes, you should convert the test into a new +library first. + +It's also much easier to review the changes if you split them into a smaller +logical groups. The same goes for moving files: if you need to rename or to move +files, do it in a separate patch. + +License +~~~~~~~ + +Code contributed to LTP should be licensed under GPLv2+ (GNU GPL version 2 or +any later version). + +Use ``SPDX-License-Identifier: GPL-2.0-or-later`` + +LTP Structure +------------- + +The structure of LTP is quite simple. Each test is a binary written either in +portable shell or C. The test gets a configuration via environment variables +and/or command line parameters, it prints additional information into the +stdout and reports overall success/failure via the exit value. + +Tests are generally placed under the :master:`testcases` directory. Everything that +is a syscall or (slightly confusingly) libc syscall wrapper, goes under +:master:`testcases/kernel/syscalls/`. + +There is also :master:`testcases/open_posix_testsuite/` which is a well maintained +fork of the Open POSIX testsuite project, that has been dead since 2005. + +We also have a number of directories with tests for more specific features, such +as containers, etc. + +Runtest Files +~~~~~~~~~~~~~ + +The list of tests to be executed is stored in runtest files under the +:master:`runtest` directory. The default set of runtest files to be executed is +stored in :master:`scenario_groups/default`. When you add a test, you should add +corresponding entries into some runtest file(s) as well. + +Each line of runtest file contains one test. The first item is the test name. +All other items, separated by space will be executed as a command. + +.. code-block:: bash + + shell_test01 echo "SUCCESS" | shell_pipe01.sh + splice02 splice02 -s 20 + +Blank lines and lines starting with a ``#`` (comments) are ignored. + +Syscalls tests, placed under :master:`testcases/kernel/syscalls/`, use +:master:`runtest/syscalls` file. For kernel related tests for memory management we +have :master:`runtest/mm`, etc. + +.. note:: + + runtest files should have one entry per a test. Creating a + wrapper that runs all your tests and adding it as a single test + into runtest file is strongly discouraged. + +Datafiles +--------- + +If your test needs data files, these should be put into a subdirectory +named ``datafiles`` and installed into the ``testcases/data/$TCID`` directory. +This will require to add ``INSTALL_DIR := testcases/data/TCID`` into +correspondent ``datafiles/Makefile``. + +You can obtain path to datafiles via ``$TST_DATAROOT`` provided by ``test.sh`` +or via C function ``tst_dataroot()`` provided by libltp: + +.. code-block:: c + + const char *dataroot = tst_dataroot(); + +Datafiles can also be accessed as ``$LTPROOT/testcases/data/$TCID/...``, +but ``$TST_DATAROOT`` and ``tst_dataroot()`` are preferred, as these can be used +when running testcases directly in git tree as well as from install location. + +Sub-executables +~~~~~~~~~~~~~~~ + +If your test needs to execute a binary, place it in the same directory of the +testcase and name the binary with ``$TESTNAME_`` prefix, where ``$TESTNAME`` is +the name of the test binary. Once the test is executed by the framework, the +path to the directory with all LTP binaries is added to the ``$PATH`` and you +can execute it via its name. + +.. note:: + + If you need to execute a test from the LTP tree, you can add ``PATH`` to + the current directory with ``PATH="$PATH:$PWD" ./foo01``. + +Test Contribution Checklist +--------------------------- + +#. Test compiles and it runs fine (check with ``-i 10`` and ``-i 0`` too) +#. ``make check`` should not emit any warnings for the test you are working on + (hint: run it in the test's directory and/or use ``make check-$TCID``) +#. The runtest entries are in place +#. New test binaries are added into the corresponding ``.gitignore`` files +#. Patches apply over the latest git + +About .gitignore files +~~~~~~~~~~~~~~~~~~~~~~ + +There are numerous ``.gitignore`` files in the LTP tree. Usually, there is a +``.gitignore`` file for a group of tests. The reason of this setup is simple: +it's easier to maintain a ``.gitignore`` file per tests' directory, rather +than having a single file in the project root directory. In this way, we don't +have to update all the gitignore files when moving directories, and they get +deleted automatically when a directory with tests is removed. + +Testing pre-release kernel features +----------------------------------- + +Tests for features not yet in the mainline kernel release are accepted. However, +they must be added only to :master:`runtest/staging`. Once a feature is part +of the stable kernel ABI, the associated test must be moved out of staging. + +Testing builds with GitHub Actions +---------------------------------- + +Master branch is tested in GitHub :repo:`actions` +to ensure LTP builds in various distributions, including old, current and +bleeding edge. ``gcc`` and ``clang`` toolchains are also tested for various +architectures using cross-compilation. For a full list of tested distros, please +check :master:`.github/workflows/ci-docker-build.yml`. + +.. note:: + + Passing the GitHub Actions CI means that LTP compiles in a variety of + different distributions on their **newest releases**. + The CI also checks for code linting, running ``make check`` in the whole + LTP project. + +LTP C And Shell Test API Comparison +----------------------------------- + +.. list-table:: + :header-rows: 1 + + * - C API :ref:`struct tst_test` members + - Shell API ``$TST_*`` variables + + * - .all_filesystems + - TST_ALL_FILESYSTEMS + + * - .bufs + - \- + + * - .caps + - \- + + * - .child_needs_reinit + - not applicable + + * - .cleanup + - TST_CLEANUP + + * - .dev_extra_opts + - TST_DEV_EXTRA_OPTS + + * - .dev_fs_opts + - TST_DEV_FS_OPTS + + * - .dev_fs_type + - TST_FS_TYPE + + * - .dev_min_size + - TST_DEVICE_SIZE + + * - .format_device + - TST_FORMAT_DEVICE + + * - .max_runtime + - TST_TIMEOUT (not exactly the same, a real timeout based on old .timeout + concept. .max_runtime has also an extra 30 sec safety margin for + teardown of the test.) + + * - .min_cpus + - not applicable + + * - .min_kver + - TST_MIN_KVER + + * - .min_mem_avail + - not applicable + + * - .mnt_flags + - TST_MNT_PARAMS + + * - .min_swap_avail + - not applicable + + * - .mntpoint | .mnt_data + - TST_MNTPOINT + + * - .mount_device + - TST_MOUNT_DEVICE + + * - .needs_cgroup_ctrls + - \- + + * - .needs_checkpoints + - TST_NEEDS_CHECKPOINTS + + * - .needs_cmds + - TST_NEEDS_CMDS + + * - .needs_devfs + - \- + + * - .needs_device + - TST_NEEDS_DEVICE + + * - .needs_drivers + - TST_NEEDS_DRIVERS + + * - .needs_kconfigs + - TST_NEEDS_KCONFIGS + + * - .needs_overlay + - \- + + * - .needs_rofs + - \- + + * - .needs_root + - TST_NEEDS_ROOT + + * - .needs_tmpdir + - TST_NEEDS_TMPDIR + + * - .options + - TST_PARSE_ARGS | TST_OPTS + + * - .resource_files + - \- + + * - .restore_wallclock + - not applicable + + * - .sample + - \- + + * - .save_restore + - \- + + * - .scall + - not applicable + + * - .setup + - TST_SETUP + + * - .skip_filesystems + - TST_SKIP_FILESYSTEMS + + * - .skip_in_compat + - \- + + * - .skip_in_lockdown + - TST_SKIP_IN_LOCKDOWN + + * - .skip_in_secureboot + - TST_SKIP_IN_SECUREBOOT + + * - .supported_archs + - not applicable + + * - .tags + - \- + + * - .taint_check + - \- + + * - .tcnt + - TST_CNT + + * - .tconf_msg + - not applicable + + * - .test | .test_all + - TST_TESTFUNC + + * - .test_variants + - \- + + * - .tst_hugepage + - not applicable + + * - .ulimit + - not applicable + + * - not applicable + - TST_NEEDS_KCONFIGS_IFS + + * - not applicable + - TST_NEEDS_MODULE + + * - not applicable + - TST_POS_ARGS + + * - not applicable + - TST_USAGE + +.. list-table:: + :header-rows: 1 + + * - C API other structs + - Shell API ``$TST_*`` variables + + * - :ref:`struct tst_device` + - TST_DEVICE diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 00000000..acd16cdb --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,112 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. include:: ../README.rst + +.. toctree:: + :maxdepth: 3 + :hidden: + :caption: For users + + users/quick_start + users/setup_tests + users/testers_guide + users/supported_systems + users/stats + users/test_catalog + +.. toctree:: + :maxdepth: 3 + :hidden: + :caption: For developers + + developers/setup_mailinglist + developers/writing_tests + developers/test_case_tutorial + developers/api_c_tests + developers/api_shell_tests + developers/api_network_tests + developers/api_kvm_tests + developers/ltp_library + developers/build_system + developers/debugging + developers/documentation + +.. toctree:: + :maxdepth: 3 + :hidden: + :caption: For maintainers + + maintainers/patch_review + maintainers/ltp_release_procedure + +For users +--------- + +.. descriptions here are active + +:doc:`users/quick_start` + How to build and use LTP framework in few steps + +:doc:`users/setup_tests` + How to setup tests execution + +:doc:`users/supported_systems` + A list of supported technologies by the LTP framework + +:doc:`users/stats` + Some LTP statistics + +:doc:`users/test_catalog` + The LTP test catalog + +For developers +-------------- + +.. descriptions here are active + +:doc:`developers/setup_mailinglist` + How to configure git and to start sending patches via :git_man:`send-email`. + +:doc:`developers/writing_tests` + Starting guide on writing tests + +:doc:`developers/test_case_tutorial` + A tutorial showing how to write a test from scratch using C API + +:doc:`developers/api_c_tests` + Walk through the C API features + +:doc:`developers/api_shell_tests` + Walk through the Shell API features + +:doc:`developers/api_network_tests` + Walk through the network API features + +:doc:`developers/api_kvm_tests` + Walk through the KVM API features + +:doc:`developers/ltp_library` + Developing new features in the LTP library + +:doc:`developers/build_system` + Short introduction to the LTP build system + +:doc:`developers/debugging` + How to debug LTP tests + +:doc:`developers/documentation` + How to use and develop LTP documentation + +For maintainers +--------------- + +:doc:`maintainers/patch_review` + Steps to follow when reviewing patches + +:doc:`maintainers/ltp_release_procedure` + Steps to follow for a new LTP release + + +Getting help +------------ +To report a problem or suggest any feature, please write at ltp@lists.linux.it diff --git a/doc/ltp-run-files.txt b/doc/ltp-run-files.txt deleted file mode 100755 index 3f405b38..00000000 --- a/doc/ltp-run-files.txt +++ /dev/null @@ -1,96 +0,0 @@ -The runtest files contain a list of test cases to be executed. - -File Format ------------ - -Lines starting with a '#' are comments and blank lines are ignored. - -Otherwise, lines start with a test name followed by white space, then some -shell script to be executed. For example - -Test Name -| Delimiter Test case argument -| | | -v v v -splice02 seq 1 20000 | splice02 splice02-temp - ^ ^ ^ - | | | - | Test case executable | - -----------Shell script------------- - -So the splice02 runtest entry pipes the output of seq into the splice02 test -executable. Most runtest entries are simpler than this, for example - -splice03 splice03 - -Here the test name and executable have the same name and no arguments have -been supplied. - -Run test files should start with a comment describing the tests they contain, -e.g. - -#DESCRIPTION:Kernel system calls - -Note that the LTP has absorbed a number of other projects. Some of these have -been fully converted to the LTP format, others have runtest files generated -for them during installation, while some use a shell script to integrate them -with the other tests. - -Test suites ------------ - - - syscalls (except epoll, see below) - - fs - - fsx - - dio - - mm - - ipc - - sched - - math - - pty - -if run network tests flag is passed these additional tests are run - - tcp_cmds - - multicast - - rpc - - nfs - -To test filesystem with LVM -- testscripts/lvmtest.sh - -Device Mapper tests - - ltpdmmapper.sh - -Network tests - - network.sh - - testcases/network/sockets/ltpSockets.sh - -other filesystem or disk type tests - - autofs1.sh - - autofs4.sh - - diskio.sh - - isofs.sh - - sysfs.sh - -AIO/DIO filesystem tests - - ltp-aiodio.sh - -Device driver tests (may not run on all platforms) - - acpi - - agp - - base - - drm - - include - - nls - - pci - - tbio - - usb - -Open_hpi_testsuite - - run_tests - -Open_posix_testsuite - - run_tests - - -testcases/kernel/syscalls/epoll - The tests require additional installation files. See the README in the epoll directory. diff --git a/doc/maintainers/ltp_release_procedure.rst b/doc/maintainers/ltp_release_procedure.rst new file mode 100644 index 00000000..6dbafa4f --- /dev/null +++ b/doc/maintainers/ltp_release_procedure.rst @@ -0,0 +1,166 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Release process +=============== + +Preparations +------------ + +The release procedure generally takes a few weeks. In the first week or two, +patches that should go into the release are reviewed and possibly merged. These +patches are either fixes or patches pointed out by the community. + +Patch review, when finished, is followed by a git freeze, which is a period +where only fixes are pushed to the git. During that period community is +expected to run a LTP pre-release tests, reports problems, and/or send fixes to +the mailing list. In this period we are especially making sure that there are +no regressions in the test results on a wide range of distributions and +architectures. + +Once the stabilization period has ended the time has finally come to proceed +with the release. + +Prepare the release notes +------------------------- + +Part of the preparation is also to write the release notes, which are then +added to the GitHub release and also sent as announcement to various mailing +lists (see below). + +Have a look at `this release letter `_ +to get the idea how it should look. + +Tag the git and push changes to github +-------------------------------------- + +.. code-block:: bash + + cd ltp + echo YYYYMMDD > VERSION + git commit -S -s -m 'LTP YYYYMMDD' VERSION + git tag -s -a YYYYMMDD -m 'LTP YYYYMMDD' + git push origin master:master + git push origin YYYYMMDD + +The string ``YYYYMMDD`` should be substituted to the current date. + +You can use :master:`tools/tag-release.sh` script to have the above automated +process. It allows you to verify the tag before pushing it and does other +checks. + +.. code-block:: bash + + $ ./tools/tag-release.sh + ===== git push ===== + new tag: 'YYYYMMDD', previous tag: '20230127' + tag YYYYMMDD + Tagger: Person-who-released LTP + Date: ... + + LTP YYYYMMDD + -----BEGIN PGP SIGNATURE----- + ... + -----END PGP SIGNATURE----- + + commit 3ebc2dfa85c2445bb68d8c0d66e33c4da1e1b3a7 + gpg: using RSA key ... + ... + Primary key fingerprint: ... + Author: Person-who-released LTP + Date: ... + + LTP YYYYMMDD + + Signed-off-by: Person-who-released LTP + + diff --git a/VERSION b/VERSION + index af4c41fec..ae488c0e7 100644 + --- a/VERSION + +++ b/VERSION + @@ -1 +1 @@ + -20230127 + +YYYYMMDD + + Please check tag and signature. Proceed? [N/y]: y + Pushing changes to upstream git. Proceed? [N/y]: y + ... + To github.com:linux-test-project/ltp.git + * [new tag] YYYYMMDD -> YYYYMMDD + +Prepare tarballs and metadata documentation +------------------------------------------- + +The following procedure will show how to create the release archives and the +metadata documentation: + +.. code-block:: bash + + # clone already clonned git repository to new folder + cd .. + git clone ltp ltp-full-YYYYMMDD + cd ltp-full-YYYYMMDD + + # update all submodules + git submodule update --init + + # Generate configure script + make autotools + + # Generate tarballs + cd .. + tar -cjf ltp-full-YYYYMMDD.tar.bz2 ltp-full-YYYYMMDD --exclude .git + tar -cJf ltp-full-YYYYMMDD.tar.xz ltp-full-YYYYMMDD --exclude .git + + # Generate checksums + md5 ltp-full-YYYYMMDD.tar.xz > ltp-full-YYYYMMDD.tar.xz.md5 + sha1 ltp-full-YYYYMMDD.tar.xz > ltp-full-YYYYMMDD.tar.xz.sha1 + sha256sum ltp-full-YYYYMMDD.tar.xz > ltp-full-YYYYMMDD.tar.xz.sha256 + +You can use :master:`tools/create-tarballs-metadata.sh` script to have the above +procedure automated. All generated files are placed in the +``ltp-release-YYYYMMDD`` directory. + +.. code-block:: bash + + $ ./tools/create-tarballs-metadata.sh + ===== git clone ===== + Cloning into 'ltp-full-YYYYMMDD'... + done. + ===== Update submodules ===== + Submodule 'tools/kirk' (https://github.com/linux-test-project/kirk.git) registered for path 'tools/kirk' + ... + ===== Generate configure script ===== + sed -n '1{s:LTP-:m4_define([LTP_VERSION],[:;s:$:]):;p;q}' VERSION > m4/ltp-version.m4 + aclocal -I m4 + ... + ===== Generate tarballs ===== + ===== Generate checksums ===== + ===== Generate metadata documentation ===== + checking for a BSD-compatible install... /usr/bin/install -c + ... + Generated files are in '/home/foo/ltp-release-YYYYMMDD', upload them to github + +Upload the generated files to GitHub +------------------------------------ + +Go to :repo:`tags`. Click on ``Add release notes``. +There should be ``Attach binaries ...`` link at the bottom of the page. + +Don't forget to upload checksums for the tarballs and metadata documentation +as well. + +Send release announcement +------------------------- + +The announcement is sent to: + +* ltp at lists.linux.it (requires a subscription) +* linux-kernel at vger.kernel.org +* libc-alpha at sourceware.org (requires a subscription) +* valgrind-developers at lists.sourceforge.net (requires a subscription) + +CCed to: + +* lwn at lwn.net +* akpm at linux-foundation.org +* torvalds at linux-foundation.org diff --git a/doc/maintainers/patch_review.rst b/doc/maintainers/patch_review.rst new file mode 100644 index 00000000..28bc4faf --- /dev/null +++ b/doc/maintainers/patch_review.rst @@ -0,0 +1,167 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Patch review +============ + +Anyone can and should review patches. It's the only way to get good at patch +review and for the project to scale. For this reason, we have a short guide on +what to do during the review process. + +Goals of patch review +--------------------- + +#. Prevent false positive test results +#. Prevent false negative test results +#. Keep the code as simple as possible, but no simpler + +How to find clear errors +------------------------ + +A clear error is one where there is unlikely to be any argument if you +provide evidence of it. Evidence being an error trace or logical proof +that an error will occur in a common situation. + +The following are examples and may not be appropriate for all tests. + +* Merge the patch locally. It should apply cleanly to master. +* Compile the patch with default and non-default configurations. + + * Use sanitizers e.g. undefined behavior, address. + * Compile on non-x86 + * Compile on x86 with ``-m32`` + * Compile testing patches with GitHub Actions in LTP repo fork can cover + various distros/architectures + +* Use ``make check`` +* Run effected tests in a VM + + * Use single vCPU + * Use many vCPUs and enable NUMA + * Restrict RAM to < 1GB. + +* Run effected tests on an embedded device +* Run effected tests on non-x86 machine in general +* Run reproducers on a kernel where the bug is present +* Run tests with ``-i0`` and ``-i2`` +* Compare usage of system calls with man page descriptions +* Compare usage of system calls with kernel code +* Double check commit message +* Search the LTP library for existing helper functions +* Check doc formatting, see :doc:`../developers/documentation`. + +How to find subtle errors +------------------------- + +A subtle error is one where you can expect some argument because you +do not have clear evidence of an error. It is best to state these as +questions and not make assertions if possible. + +Although if it is a matter of style or "taste" then senior maintainers +can assert what is correct to avoid bike shedding. + +* Ask what happens if there is an error, could it be debugged just + with the test output? +* Are we testing undefined behavior? + + * Could future kernel behavior change without "breaking userland"? + * Does the kernel behave differently depending on hardware? + * Does it behave differently depending on kernel configuration? + * Does it behave differently depending on the compiler? + * Would it behave differently if the order of checks on syscall parameters + changed in the kernel? + +* Will it scale to tiny and huge systems? + + * What happens if there are 100+ CPUs? + * What happens if each CPU core is very slow? + * What happens if there are 2TB of RAM? + +* Are we repeating a pattern that can be turned into a library function? +* Is a single test trying to do too much? +* Could multiple similar tests be merged? +* Race conditions + + * What happens if a process gets preempted? + * Could checkpoints or fuzzsync by used instead? + * Note, usually you can insert a sleep to prove a race condition + exists however finding them is hard + +* Is there a simpler way to achieve the same kernel coverage? + +How to get patches merged +------------------------- + +Once you think a patch is good enough you should add your ``Reviewed-by`` +and/or ``Tested-by`` tags. This means you will get some credit for getting +the patch merged. Also some blame if there are problems. + +If you ran the test you can add the ``Tested-by`` tag. If you read the +code or used static analysis tools on it, you can add the Reviewed-by +tag. + +In addition you can expect others to review your patches and add their +tags. This will speed up the process of getting your patches merged. + +Maintainers Checklist +--------------------- + +Patchset should be tested locally and ideally also in maintainer's fork in +GitHub Actions on GitHub. + +.. note:: + + GitHub Actions do only build testing, passing the CI means only that + the test compiles fine on variety of different distributions and releases. + +The test should be executed at least once locally and should PASS as well. + +Commit messages should have + +* Author's ``Signed-off-by`` tag +* Committer's ``Reviewed-by`` or ``Signed-off-by`` tag +* Check also mailing lists for other reviewers / testers tags, notes and failure + reports +* ``Fixes: hash`` if it fixes particular LTP commit +* ``Fixes: #N`` if it fixes github issue number N, so it's automatically closed +* LTP documentation should be kept up to date. + +After patch is accepted or rejected, set correct state and archive in the +`LTP patchwork instance `_. + +New tests +--------- + +New test should + +* Have a record in runtest file +* Test should work fine with more than one iteration (e.g. run with ``-i 100``) +* Run with ``-i 0`` to check that setup and cleanup are coded properly + (no test is being run) +* Have a brief description +* License: the default license for new tests is GPL v2 or later, use + ``GPL-2.0-or-later``; the license for test (e.g. GPL-2.0) should not change + unless test is completely rewritten +* Old copyrights should be kept unless test is completely rewritten + +C tests +~~~~~~~ + +* Use :doc:`../developers/api_c_tests`, implementing :ref:`struct tst_test` +* Test binaries are added into corresponding ``.gitignore`` files +* Check coding style with ``make check`` +* Metadata documentation +* If a test is a regression test it should include :ref:`.tags` in the + :ref:`struct tst_test` definition + +Shell tests +~~~~~~~~~~~ + +* Use :doc:`../developers/api_shell_tests` +* Check coding style with ``make check`` +* If a test is a regression test it should include related kernel or glibc + commits as a comment + +LTP library +~~~~~~~~~~~ + +For patchset touching the LTP library, follow :doc:`../developers/ltp_library`. diff --git a/doc/man1/Makefile b/doc/man1/Makefile deleted file mode 100755 index ecd2b6da..00000000 --- a/doc/man1/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# -# man1 Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Ngie Cooper, July 2009 -# - -MANPREFIX := 1 - -top_srcdir ?= ../.. - -include $(top_srcdir)/include/mk/man.mk diff --git a/doc/man1/doio.1 b/doc/man1/doio.1 deleted file mode 100755 index e488c03f..00000000 --- a/doc/man1/doio.1 +++ /dev/null @@ -1,70 +0,0 @@ -.\" -.\" $Id: doio.1,v 1.1 2000/07/27 16:59:03 alaffin Exp $ -.\" -.\" 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/ -.\" -.TH doio 1 10/13/93 "UNICOS Testing" -.SH NAME -\*Cdoio\fR - Executes I/O Requests -.SH IMPLEMENTATION -All Cray Research systems -.SH SYNOPSIS -\*Cdoio\fR -.SH DESCRIPTION -.QS -Doio is one of the device-beater tools. -.PP -Options: -.RS .5i -.IP "-a" -abort on data compare errors -.IP "-n opt" -.IP "-k opt" -lockd request pipe -.IP "-K opt" -use fcntl() file locking -.IP "-r opt" -resource release interval -.IP "-s opt" -syscall log file -.IP "-w opt" -file write log file -.IP "-v" -verify writes if set -.IP "-U opt" -upanic() on varios conditions -.RE -.SH AUTHOR -Mark Maule wrote the code. -.br -Glen Overby wrote the man page. -.SH BUGS -See "Features". diff --git a/doc/man1/iogen.1 b/doc/man1/iogen.1 deleted file mode 100755 index f5941571..00000000 --- a/doc/man1/iogen.1 +++ /dev/null @@ -1,78 +0,0 @@ -.\" -.\" $Id: iogen.1,v 1.1 2000/07/27 16:59:03 alaffin Exp $ -.\" -.\" 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/ -.\" -.TH iogen 1 10/13/93 "UNICOS Testing" -.SH NAME -\*Ciogen\fR - Generate I/O Requests -.SH IMPLEMENTATION -All Cray Research systems -.SH SYNOPSIS -\*Ciogen\fR -.SH DESCRIPTION -.QS -Iogen is one of the device-beater tools. -.PP -Options: -.RS .5i -.IP "-f [opt]" -open flags: -raw, sync, ssd, ldraw, buffered -.IP "-i [opt]" -Number of iterations to run. -0 implies infinite. -.IP "-q" -.IP "-m [opt]" -Offset mode. -One of: random, sequential, reverse. -.IP "-o" -Overlap flag. -.IP "-p [opt]" -output pipe (default is stdout) -.IP "-r [opt]" -specify raw io multiple instead of getting it from the mounted on device. -Only applies to regular files. -.IP "-s [opt]" -System calls to use for I/O. -A list of: -read, write, reada, writea, ssread, sswrite -.IP "-t [opt]" -min transfer size -.IP "-T [opt]" -max transfer size -.RE -.SH AUTHOR -Mark Maule wrote the code. -.br -Glen Overby wrote the man page. -.SH BUGS -See "Features". diff --git a/doc/man1/ltp-bump.1 b/doc/man1/ltp-bump.1 deleted file mode 100755 index aef2b4a3..00000000 --- a/doc/man1/ltp-bump.1 +++ /dev/null @@ -1,80 +0,0 @@ -.\" -.\" $Id: ltp-bump.1,v 1.1 2009/05/19 09:39:11 subrata_modak Exp $ -.\" -.\" 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/ -.\" -.TH BUMP 1 "14 Sep 2000" "LTP" "Linux Test Project" -.SH NAME -ltp-bump \- send signal to tags run by ltp-pan -.SH SYNOPSIS -\fBltp-bump [-1] [-s \fIsig\fB] [\fI-a active-file\fB] [tags...] -.SH DESCRIPTION - -Bump will send a SIGINT signal to processes, given that each process has a -corresponding tag in an active-file. The active-file is the same one that is -used by the ltp-pan to start the processes. - -If the active file has multiple occurrences of a single tag name then only the -first process will be signaled. You may specify the tag name multiple times -on the commandline if necessary. - -.TP 1i -\fB-1\fP -Send a SIGUSR1 signal. By default a SIGINT will be sent. -.TP 1i -\fB-a \fIactive_file\fB -A file containing the tagnames, pids, and commands being run by a ltp-pan. If this -is not specified then the ZOO environment variable will be read for the name of -the directory where the active file can be found. -.TP 1i -\fB-s \fIsig\fB -Used to specify a signal number to send. By default a SIGINT will be sent. - -.in -1i - -.SH ENVIRONMENT -.TP -ZOO -If set, should name the directory where the active file can be found. -This is ignored if \fI-a\fP is specified. - -.SH FILES -.TP -active -Default name of active file if \fI-a\fP is not specified. This is prefixed -by the directory name found in the ZOO environment variable. - -.SH "SEE ALSO" -Zoo tools - ltp-pan(1) - -.SH DIAGNOSTICS -Exits zero, unless it cannot find the active file or if there were no tags -listed on the commandline. diff --git a/doc/man1/ltp-pan.1 b/doc/man1/ltp-pan.1 deleted file mode 100755 index 5ffa57db..00000000 --- a/doc/man1/ltp-pan.1 +++ /dev/null @@ -1,262 +0,0 @@ -.\" -.\" $Id: ltp-pan.1,v 1.1 2009/05/19 09:39:11 subrata_modak Exp $ -.\" -.\" 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/ -.TH PAN 1 "21 Jan 2011" "LTP" "Linux Test Project" -.SH NAME -ltp-pan \- A light-weight driver to run tests and clean up their pgrps -.SH SYNOPSIS -\fBltp-pan -n tagname [-SyAehp] [-t #s|m|h|d \fItime\fB] [-s \fIstarts\fB] [\fI-x nactive\fB] [\fI-l logfile\fB] [\fI-a active-file\fB] [\fI-f command-file\fB] [\fI-d debug-level\fB] [\fI-o output-file\fB] [\fI-O buffer_directory\fB] [\fI-r report_type\fB] [\fI-C fail-command-file\fB] [cmd] -.SH DESCRIPTION - -Pan will run a command, as specified on the commandline, or collection of -commands from a command-file. By default ltp-pan runs one command, choosing it at -random from the whole set of commands available to it. The ltp-pan's name in the -active file is specified by the tagname. When a command terminates ltp-pan will -kill any orphans that may have been left behind in its pgrp. If ltp-pan is -signaled it will kill any active commands and, again, clean up any orphans. - -Pan uses the signal ratchet found in other zoo tools. The first time ltp-pan is -signaled it sends a SIGTERM to the active pgrps; the second time it sends -SIGHUP; the third time a SIGINT; after that it always sends SIGKILL. - -Pan will not terminate until all the active commands and everything in their -pgrps is dead. It will loop around at 5 second intervals, triggering its own -signal ratchet, until it succeeds in killing the pgrps. - -When the ltp-pan starts up it places its own tagname and commandline in the active -file and begins scheduling commands. After a command is started ltp-pan puts an -entry for it into the active file with its indicated tagname. If the command -was specified on the command line, rather than in the command-file, then its -tagname will be "cmdln". When a process terminates ltp-pan frees the active file -entry. If a command terminates and leaves an orphaned pgrp then ltp-pan will put -an entry into the active file called "panorphan" which will be removed only -when the orphaned pgrp is cleaned up. Before ltp-pan exits it will ensure that -all orphaned pgrps are dead (see above) and then it will remove its own -tagname from the active file. - -The command-file is a file containing tag/command pairs. Each line in the -file begins with a tag identifying the command, followed by white space, and -then the command and its arguments. A line beginning with the # character is -a comment. Pan recognizes the token "%f" in a command's arguments and -replaces it with a unique identifier--add this to filename arguments to -prevent two instances of the command from interfering with each other. - -When ltp-pan receives a SIGUSR2 it stops scheduling new tests and waits for the -active tests to terminate. If the \fB-y\fP option was used then it will begin -scheduling again, otherwise it will exit. It does not propagate the SIGUSR2. - -.TP 1i -\fB-A\fP -The all-stop flag. If any command exits non-zero ltp-pan will shutdown its -scheduler and signal any active pgrps. The ltp-pan will exit non-zero after -everything is shut down. By default ltp-pan ignores command exit statuses. -The \fI-e\fP option is implied when this option is used. -.TP 1i -\fB-a \fIactive_file\fB -A file containing the tagnames, pids, and commands being run. If this is -not specified then the ZOO environment variable will be read for the name -of a directory where the active file will be placed, and in this case the -active file's name will be "active". A single active file may be shared -by any number of Zoo tools. -.TP 1i -\fB-C \fIfail-command-file\fB -The file to which all failed test commands will be saved. You can use it later with \fI-f\fP option if you want to run only the failed test cases. -.TP 1i -\fB-d \fIdebug-level\fB -See the source for settings. -.TP 1i -\fB-e\fP -Pan will exit non-zero if any of its commands exited non-zero. By default -ltp-pan ignores command exit statuses. -.TP 1i -\fB-f \fIcommand-file\fB -The file that has a collection of commands that ltp-pan will execute. -.TP 1i -\fB-h\fP -Print some simple help. -.TP 1i -\fB-l \fIlogfile\fB -Name of a log file to be used to store exit information for each of the -commands (tags) that are run. This log file may not be shared with other Zoo -tools or other ltp-pan processes. -.TP 1i -\fB-n \fItagname\fB -The tagname by which this ltp-pan process will be known by the zoo tools. This -is a required argument. -.TP 1i -\fB-o \fIoutput_file\fB -The file to which all test output will be saved. Normally all test output is sent to standard output. This includes each test's standard output and standard error. -.TP 1i -\fB-O \fIbuffer_directory\fB -A directory where ltp-pan can place temporary files to capture test output. This will prevent output from several tests mixing together in the output file. -.TP 1i -\fB-p\fP -Enables printing results in human readable format. -.TP 1i -\fB-r \fIreport_type\fB -This controls the type of output that ltp-pan will produce. Supported formats are \fIrts\fP and \fInone\fP. The default is \fIrts\fP. -.TP 1i -\fB-S\fP -Causes ltp-pan to run commands (tags) sequentially, as they are listed in the -command-file. By default it chooses tags randomly. If a command is specified -on the commandline and a command-file is also specified, then the commandline -tag will be the last command. If this is specified and \fI-s\fP is not -specified then the default setting for \fI-s\fP is equal to the total number -of commands. -.TP 1i -\fB-s \fIstarts\fB -Indicates the number of commands (tags) that should be run before terminating. -Set this to zero to run forever. By default this is set to 1 (but see -\fI-S\fP for an exception). If this is specified and is less than the value -specified for \fI-x\fP then it is bumped up to be equal to the value of -\fI-x\fP (in other words, \fI-x\fP is always satisfied). -.TP 1i -\fB-t #s|m|h|d \fItime\fB -Indicates the length that ltp-pan should run tests. By default this is not set. If specified, -the \fI-s\fP flag is automatically set to 0 (infinite). Presumably, you want as many -tests ran during this timeframe. Duration is measured in \fIs\fPeconds, \fIm\fPinutes, -\fIh\fPours, or \fId\fPays. -.TP 1i -\fB-x \fInactive\fB -Indicates the number of commands (tags) that should be kept active at any one -time. If this is greater than 1 then it is possible to have multiple -instances of the same tag active at once. By default this is 1. -.TP 1i -\fB-y\fP -Causes the ltp-pan scheduler to go idle if a signal is received or if a command -exits non-zero. All active commands and their pgrps will be killed. After -everything is dead the scheduler will restart again where it left off. If the -signal is SIGUSR1 then ltp-pan will behave as if \fI-y\fP had not been specified. - -.in -1i - -.SH EXAMPLES - -In practice, the ZOO environment variable is generally preferred over the -\fI-a\fP option. All examples assume this is being set. - -The following creates a ltp-pan named "ex1" with an active file in /tmp/active. -It runs the command "echo hello", keeping 3 copies running at all times, -running 10 copies before terminating. - -$ export ZOO=/tmp -.br -$ ltp-pan -n ex1 -s 10 -x 3 echo hello - -The next example will use this command file. Call this /tmp/cmds1. -.br -----------cut------ -.br -fido ls /bin -.br -rover echo hello wally -.br -gidget sleep 2 -.br -lassie ls /etc -.br -----------cut------ -.br - -Using the above command file, /tmp/cmds1, run one command at a time, -sequentially, running each command only once. If one command should fail then -terminate immediately. An exit log is kept for all the commands. - -$ ltp-pan -n ex3 -S -A -f /tmp/cmds1 -l ex3.log - -Here is just a simple stress case. In this case the test will run for 24 hours, -printing the output as a human readable format, with the test output at /tmp/output-file -and all failed test commands (if you have any) at /tmp/fail-command-file. - -$ ltp-pan -n stress -e -p -q -S -t 24h -a stress -l logfile -f command-file \ - -o /tmp/output-file -C /tmp/fail-command-file - -.SH LAYERING - -Pan is often used in layers. This section extends the above examples to show -how this is done. - -The next example will use this command file. Call this /tmp/cmds2. Note that -the embedded ltp-pans inside this file have exit logs, and that %f is used to give -each ltp-pan a unique log file name. -.br -----------cut------ -.br -larry ltp-pan -n ex4b -s10 -A -l ex4_%f.log echo hello -.br -curly ltp-pan -n ex4c -S -A -f /tmp/cmds1 -l ex4_%f.log -.br -moe echo done here -.br -----------cut------ -.br - -The following will run commands from the command file, keeping two at a time -running, choosing them sequentially, and terminating if any of them exits -non-zero. - -$ ltp-pan -n ex4 -x2 -A -S -f /tmp/cmds2 - -Now run the commands in /tmp/cmds2, but this time we want to recover if one of -the commands should exit non-zero. In this example it is possible for the -"larry" or "curly" tags to exit non-zero. When this happens the ltp-pan will kill -all active tags, making sure both larry and curly are dead, and then will -continue scheduling--ensuring that our "done here" message comes out no matter -what. - -$ ltp-pan -n ex5 -x2 -A -S -y -f /tmp/cmds2 - -.SH ENVIRONMENT -.TP -ZOO -If set, should name the directory where the active file should be placed. -This is ignored if \fI-a\fP is specified. - -.SH FILES -.TP -active -Default name of active file if \fI-a\fP is not specified. This is prefixed -by the directory name found in the ZOO environment variable. -.TP -PAN_STOP_FILE -The creation of this file in the defined \fITMP\fP directory will cause ltp-pan to -execute one more loop and stop. This is useful when testing needs to be stopped -before its scheduled stop time (\fI-t\fP). By doing a 'touch' on this file, testing -is ended, i.e. touch /tmp/runalltests-2345/PAN_STOP_FILE - -.SH "SEE ALSO" -Zoo tools - ltp-bump(1) - -.SH DIAGNOSTICS -By default it exits zero unless signaled, regardless of the exit status of any -of the commands it is running. If \fI-A\fP or \fI-e\fP are specified it exits non-zero if -it is signaled or if any of the commands it is running should exit non-zero. diff --git a/doc/man3/Makefile b/doc/man3/Makefile deleted file mode 100755 index ff36ddd1..00000000 --- a/doc/man3/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# -# man3 Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Ngie Cooper, July 2009 -# - -MANPREFIX := 3 - -top_srcdir ?= ../.. - -include $(top_srcdir)/include/mk/man.mk diff --git a/doc/man3/parse_opts.3 b/doc/man3/parse_opts.3 deleted file mode 100755 index b3472ce9..00000000 --- a/doc/man3/parse_opts.3 +++ /dev/null @@ -1,179 +0,0 @@ -.\" -.\" $Id: parse_opts.3,v 1.3 2000/08/31 18:40:28 nstraz Exp $ -.\" -.\" 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/ -.\" -.TH PARSE_OPTS 3 "21 Jan 2011" "LTP" "Linux Test Project" -.SH NAME -parse_opts \- parse standard and user options for LTP test programs -.SH SYNOPSIS -.nf -.B #include \(rqtest.h\(rq -.B #include \(rqusctest.h\(rq -.sp -.BI "char *parse_opts(int " argc ", char *" argv[] ", " -.BI " option_t " option_array[] "," -.BI " void (*" user_help_func ")());" -.fi -.SH DESCRIPTION -The \fBparse_opts()\fP routine parses options from the command line, looking -for user specified options or standard options (see below). Its arguments -\fIargc\fP and \fIargv\fP are the argument count and array as passed to the -main() function on program invocation. User options may be specified in the -\fIoption_array\fR argument. A help function may be specified in the -\fIuser_help_func\fP argument. -.sp -\fIoption_array\fP is a pointer to the first element of an array of -\fBoption_t\fP. If no additional options are needed, pass NULL. -\fBoption_t\fR is declared in \fBusctest.h\fP as -.nf -.sp -.in 10 -typedef struct { -.in 14 -char *option; -int *flag; -char **arg; -.in 10 -} option_t; -.fi -.PP -The meanings of the different fields are: -.TP -.I option -is a valid option string to be given to getopt(). -.TP -.I flag -is a pointer to an integer location to set true if option is given in -\fIargv\fR. This can be NULL if the option doesn't require an argument. -.TP -.I arg -is a pointer to a character pointer variable that will be set with the option -argument if the option is present in argv. This pointer MUST be provided if -the option can take an argument. Failure to provide a location will cause -\fBparse_opts()\fR to return an error. -.PP -.I user_help_func -is a pointer to a function that will be called when the \-h option is found. -This function should print help messages for the options in \fIoption_array\fR -to standard out. The standard help messages are formatted such that the option -designator starts in column 3 and the description starts in column 11. -.sp -.SH "STANDARD OPTIONS" -Below is a list of the standard options defined in \fBparse_opts()\fR: -.TP -.BI \-c " n" -Run \fIn\fR copies of the test in parallel. This is done by forking \fIn\fR -times and running the test as usual. If \-i or \-I are specified, each process -will run for that amount of time. -.TP -.B \-e -Turn on logging all errno's received. This option is to facilitate security -audit testing for MLS. -.TP -.B \-f -Suppresses functional testing messages. -.TP -.B \-h -Print help message. The standard options will be printed first, then a call to -.I user_help_func() -will be made. -.TP -.BI \-i " n" -Run for \fIn\fR iterations. A value of 0 makes the test loop infinitely. -(default 1) -.TP -.BI \-I " x" -The test will loop until \fIx\fR seconds have passed. (default 0.0) -.TP -.B \-p -Pause for SIGUSR1 before testing. The test will pause where you place -TEST_PAUSE. \fIWarning\fR: The test will also fork at this point if \-c is -used. -.TP -.BI \-P " x" -This option will do a delay of \fIx\fR seconds after each iteration. (default 0.0) -.TP -.B \-t -Produce timing statistics. *NOT IMPLEMENTED* -.PP -.sp -The STD_* flags are used by system call test macros defined in usctest.h -(see \fBusctest(3)\fR), or may be used in the user's code. -.SH "RETURN VALUE" -.B parse_opts() -returns a NULL pointer upon successful completion. If an error occurs a -pointer to an error message is returned. -.SH "EXAMPLE" -The following example defines two options, one with an argument, one without. -.sp -.nf -int fflag, Tflag; /* binary flags: opt or not */ -char *Topt; /* option arguments */ - -option_t options[] = { - { "F", &fflag, NULL }, /* No argument */ - { "T:", &Tflag, &Topt }, /* argument required */ - { NULL, NULL, NULL } /* NULL required to end array */ -}; - -void help() -{ - printf(" -F An option with no argument\\n"); - printf(" -T opt An option that requires an argument\\n"); -} - -int main(int argc, char *argv[]) -{ - char *msg; - - if ((msg = parse_opts(argc, argv, options, &help)) != NULL) - error_exit(msg); - - return 0; -} -.fi -.sp -The following example shows how to use \fBparse_opts()\fR without defining new options. -.sp -.nf -int main(int argc, char *argv[]) -{ - char *msg; - - if ((msg = parse_opts(argc, argv, NULL, NULL)) != NULL) - error_exit(msg); - - return 0; -} -.fi -.SH "SEE ALSO" -usctest(3), getopt(3). diff --git a/doc/man3/parse_ranges.3 b/doc/man3/parse_ranges.3 deleted file mode 100755 index d92e001f..00000000 --- a/doc/man3/parse_ranges.3 +++ /dev/null @@ -1,169 +0,0 @@ -.\" -.\" $Id: parse_ranges.3,v 1.1 2000/07/27 16:59:03 alaffin Exp $ -.\" -.\" 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/ -.\" -.TH PARSE_RANGES 3 07/25/2000 "Linux Test Project" -.SH NAME -parse_ranges \- function to parse a string formatted like 'min:max:mult,...' -.SH SYNOPSIS -.nf -int parse_ranges(char *str, int defmin, int defmax, int defmult, int (*parse_func)(), char **rangeptr, char **errptr); -int range_min(char *rbuf, int r); -int range_max(char *rbuf, int r); -int range_mult(char *rbuf, int r); -.fi -.SH DESCRIPTION -parse_ranges() is a function to parse a comma-separated list of range -tokens each having the following form: -.SP -.nf - num - or - min:max[:mult] -.fi - -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. - -If \fIrange_ptr\fP is not NULL, and parse_ranges() successfully parses the -range string (ie. does not return -1), *range_ptr will point to space -malloc'd by parse_ranges(). The user may free this space by calling free(). - -parse_ranges() parameters are: -.SP -.TP 1i -\fIstr\fP -The string to parse - assumed to be a comma-separated -list of tokens having the above format. -.TP 1i -\fIdefmin\fP -default value to plug in for min, if it is missing -.TP 1i -\fIdefmax\fP -default value to plug in for max, if it is missing -.TP 1i -\fIdefmult\fP -default value to plug in for mult, if missing -.TP 1i -\fIparse_func\fP -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: -.SP -.nf - int parse_func(char *str, int *val) -.fi -.SP -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 sscanf %i format. -.TP 1i -\fIrange_ptr\fP -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. -.TP 1i -\fIerrptr\fP -user-supplied char ** which can be set to point to a -static error string. If errptr is NULL, it is ignored. -.in -1i - -.SP -range_min(), range_max(), and range_mult() parameters are: -.SP -.SP -.TP 1i -\fIrbuf\fP -An array of ranges set up by parse_ranges(). -.TP 1i -\fIr\fP -The range number to extract information from. Must be an integer >= 0 and -< the number of ranges returned by parse_ranges(). -.in -1i - -.SH EXAMPLES -\fC -.ta .25i +.25i +.25i +.25i -.nf -/* - * simple example to take a list of ranges on the cmdline (in argv[1]), and - * print a random number from within that range. - */ - -#include - -main() -{ - extern int parse_ranges(), range_min(), range_max(), range_mult(); - extern long random_range(), random_range_seed(); - int min, max, mult, nranges; - char *ep, *rp; - - random_range_seed(getpid()); - if ((nranges = parse_ranges(argv[1], 0, INT_MAX, 1, NULL, &rp, &ep)) < 0) { - fprintf(stderr, "parse_ranges() failed: %s\n", ep); - exit(1); - } - - range = random_range(0, nranges-1, 1); - min = range_min(rp, range); - max = range_max(rp, range); - mult = range_mult(rp, range); - - fprintf("%d\\n", random_range(min, max-1, mult)); - exit(0); -} -\fP -.DT -.SH "SEE ALSO" -random_range(3), -random_range_seed(3), -bytes_by_prefix(3). -.SH DIAGNOSTICS -parse_ranges() returns -1 on error or the number of ranges parsed. No space -will be malloc'd if parse_ranges() fails. Error -messages are passed back through the errptr parameter. There are no error -conditions for range_min(), range_max(), or range_mult(). diff --git a/doc/man3/random_range.3 b/doc/man3/random_range.3 deleted file mode 100755 index e7ab6727..00000000 --- a/doc/man3/random_range.3 +++ /dev/null @@ -1,114 +0,0 @@ -.\" -.\" $Id: random_range.3,v 1.1 2000/07/27 16:59:03 alaffin Exp $ -.\" -.\" 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/ -.\" -.TH random_range 3 07/25/2000 "Linux Test Project" -.SH NAME -random_range \- a set of routines for dealing with integer ranges, and random numbers in a range -.SH SYNOPSIS -.nf -void random_range_seed(int seed) -long random_range(int min, int max, int mult, char **errp) -long random_rangel(long min, long max, long mult, char **errp) -long long random_rangell(long long min, long long max, - long long mult, char **errp) -long random_bit(long mask) -.fi -.SH DESCRIPTION -This is a set of routines for parsing numeric ranges, and choosing random -numbers from a range. - -random_range() chooses a random number in the range min-max (inclusive) which -is a multiple of mult. min and max may be any integer, but mult must be -a positive integer greater than 0. errp is a char ** which is used to detect -error conditions. If errp is not NULL, *errp will be set to point to an -error message. If errp is NULL, error conditions cannot be detected by the -caller. If mult is 1 (the most common case), there are no possible error -conditions, and the return value is guaranteed to be valid. - -random_range_seed() sets the random number generator seed to the specified -value. - -random_bit() will return a randomly selected single bit bitmask from the bits -set in mask. The bit is randomly chosen using random_range(). -If mask is zero, zero is returned. - -random_range() functions uses lrand48() internally. If the range is bigger -than will fit in a 32 bit long (2G), lrand48() with a -a internal recursive algorithm to produce a random number. - -.SH EXAMPLES -\fC -.ta .25i +.25i +.25i +.25i -.nf -#include - -main(argc, argv) -int argc; -char **argv; -{ - int r; - char *errp; - extern void random_range_seed(); - extern long random_range(); - - random_range_seed(getpid()); - - r = random_range(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), &errp); - if (errp == NULL) { - fprintf(stderr, "random_range failed: %s\n", errp); - exit(1); - } else { - printf("%d\n", r); - } - - exit(0); -} -\fP -.fi - -.SH "SEE ALSO" -lrand48(3c) -.SH DIAGNOSTICS -If random_range() fails, errp will point to NULL, and the return value will be -undefined. If mult is 1, there are no possible error conditions, so the return -value is always valid in this case. - -.SH BUGS -On CRAY systems, random_range(), random_rangel(), random_rangell() -all have the 64 bit limit since int, long and long long are always 64 bits. - -On IRIX systems, random_range() can only produce a 32 number. -random_rangel() when compiled as a 32 bit object is still limited to 32 bit -number. random_rangell() can be used to return a value bigger than 32 bits -even when compiled as a 32 bit object. - diff --git a/doc/man3/random_range_seed.3 b/doc/man3/random_range_seed.3 deleted file mode 100755 index 2344b570..00000000 --- a/doc/man3/random_range_seed.3 +++ /dev/null @@ -1,114 +0,0 @@ -.\" -.\" $Id: random_range_seed.3,v 1.1 2000/07/27 16:59:03 alaffin Exp $ -.\" -.\" 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/ -.\" -.TH random_range 3 07/25/2000 "Linux Test Project" -.SH NAME -random_range \- a set of routines for dealing with integer ranges, and random numbers in a range -.SH SYNOPSIS -.nf -void random_range_seed(int seed) -long random_range(int min, int max, int mult, char **errp) -long random_rangel(long min, long max, long mult, char **errp) -long long random_rangell(long long min, long long max, - long long mult, char **errp) -long random_bit(long mask) -.fi -.SH DESCRIPTION -This is a set of routines for parsing numeric ranges, and choosing random -numbers from a range. - -random_range() chooses a random number in the range min-max (inclusive) which -is a multiple of mult. min and max may be any integer, but mult must be -a positive integer greater than 0. errp is a char ** which is used to detect -error conditions. If errp is not NULL, *errp will be set to point to an -error message. If errp is NULL, error conditions cannot be detected by the -caller. If mult is 1 (the most common case), there are no possible error -conditions, and the return value is guaranteed to be valid. - -random_range_seed() sets the random number generator seed to the specified -value. - -random_bit() will return a randomly selected single bit bitmask from the bits -set in mask. The bit is randomly chosen using random_range(). -If mask is zero, zero is returned. - -random_range() functions uses lrand48() internally. If the range is bigger -than will fit in a 32 bit long (2G), lrand48() with a -a internal recursive algorithm to produce a random number. - -.SH EXAMPLES -\fC -.ta .25i +.25i +.25i +.25i -.nf -#include - -main(argc, argv) -int argc; -char **argv; -{ - int r; - char *errp; - extern void random_range_seed(); - extern long random_range(); - - random_range_seed(getpid()); - - r = random_range(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), &errp); - if (errp == NULL) { - fprintf(stderr, "random_range failed: %s\n", errp); - exit(1); - } else { - printf("%d\n", r); - } - - exit(0); -} -\fP -.fi - -.SH "SEE ALSO" -lrand48(3c) -.SH DIAGNOSTICS -If random_range() fails, errp will point to NULL, and the return value will be -undefined. If mult is 1, there are no possible error conditions, so the return -value is always valid in this case. - -.SH BUGS -On CRAY systems, random_range(), random_rangel(), random_rangell() -all have the 64 bit limit since int, long and long long are always 64 bits. - -On IRIX systems, random_range() can only produce a 32 number. -random_rangel() when compiled as a 32 bit object is still limited to 32 bit -number. random_rangell() can be used to return a value bigger than 32 bits -even when compiled as a 32 bit object. - diff --git a/doc/man3/tst_res.3 b/doc/man3/tst_res.3 deleted file mode 100755 index 56f72243..00000000 --- a/doc/man3/tst_res.3 +++ /dev/null @@ -1,313 +0,0 @@ -.\" -.\" $Id: tst_res.3,v 1.2 2008/06/10 05:52:02 subrata_modak Exp $ -.\" -.\" 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/ -.\" -.TH TST_RES 3 01/21/2011 "Linux Test Project" -.SH NAME -tst_resm \- Print result message -.sp -tst_resm_hexd \- Print result message, including specified buffer in hexadecimal format -.sp -tst_brkm \- Print result message and break remaining test cases -.sp -tst_old_flush \- Print any messages pending because of CONDENSE mode, and flush output stream -.sp -tst_exit \- Exit test with a meaningful exit value -.sp -tst_environ \- Keep results coming to original stdout -.SH SYNOPSIS -\fB#include "test.h"\fR -.P -.P -\fBvoid tst_resm(int \fIttype\fB, char *\fItmesg, [arg ...]\fR) -.P -\fBvoid tst_resm_hexd(int \fIttype\fB, const void *\fIbuf\fB, size_t \fIsize\fB, -char *\fItmesg, [arg ...]\fR) -.P -\fBvoid tst_brkm(int \fIttype\fB, void (*\fIfunc\fB)(), char *\fItmesg, -[arg ...]\fR) -.P -\fBvoid tst_old_flush() -.P -\fBvoid tst_exit() -.P -\fBint tst_environ() -.P -\fBextern int tst_count; -.br -extern int tst_range; -.br -\fR -.SH DESCRIPTION -.SS Introduction -This library of functions are used by UNICOS tests to report results to -standard output in a consistent manner. It is assumed that tests using this -library have a distinct number of test cases, and that each test case is -distinct and uniquely identified by the test case number. It is also assumed -that test case results are printed in consecutive order, starting with 1. -The library maintains a set of global variables (\fBTCID\fR, \fBTST_TOTAL\fR, -\fBtst_count\fR), which are used by the various functions to format the -results and to keep track of the current result reporting state (i.e. how many -total test cases there are, and how many have been reported so far) for the -calling test. -.P -The \fBTCID\fR and \fBTST_TOTAL\fR global variables are externed in the -library, and MUST be defined/initialized by tests using the library. -\fBTCID\fR must be set to the \fBT\fRest \fBC\fRase \fBID\fRentifier, and -\fBTST_TOTAL\fR must be set to the total number of test cases that will be -reported. -.P -The other global variables are available as externs to tests linked with -tst_res.o. \fBtst_count\fR is the running count of the number of test -results that have been reported so far. The library initializes it to 0, and -it should not be modified by the test. -The details are described below under the appropriate functions. -.SS Arguments -.RS 5 -.TP 10 -.I ttype -test result type; one of \fBTPASS, TFAIL, TBROK, TCONF, TWARN\fR, or -\fBTINFO\fR (explained below). -.TP 10 -.I fname -pointer to a character string holding the name of a file whose contents will -be printed on a new line following \fItmesg\fR. If this pointer is NULL, it -is ignored. -.TP 10 -.I tmesg, [arg ...] -pointer to a character string containing a message explaining the test -result. This character string can contain percent-sign conversion -specifications as in \fBprintf\fR(3C) with corresponding \fIarg\fR arguments -following the \fItmesg\fR argument. -.br -\fBNOTE:\fR These routines use static space internally to hold the -expanded message. The maximum size allowed for an expanded message is -2048 bytes. -.TP 10 -.I func -pointer to a function which performs either the cleanup necessary prior to -exiting the test or some function executed at the end of each iteration of a -loop. -.TP 10 -.I buf -pointer to a buffer whose contents will be printed in hexadecimal format. -.TP 10 -.I size -size of the buffer. -.RE -.SS Result Types -The possible test result types defined in \fBtest.h\fR are as follows: -.RS 5 -.TP 10 -.B TPASS -The test case produced expected results. -.TP 10 -.B TFAIL -The test case produced unexpected results. -.TP 10 -.B TBROK -A resource needed to execute the test case was not available (e.g. a -temporary file could not be opened). -.TP 10 -.B TCONF -The test case was not appropriate for the current hardware or software -configuration (e.g. MLS was not enabled). -.TP 10 -.B TWARN -The testing procedure caused undesirable side effects that did not affect -test results (e.g. a temporary file could not be removed after all test -results were recorded). -.TP 10 -.B TINFO -An informative message about the execution of the test that does not -correspond to a test case result and does not indicate a problem. -.RE -.SS Function Descriptions - -\fBtst_resm()\fR and \fBtst_resm_hexd()\fR are the basic -result reporting functions. They report 1 or more test case results of the -specified \fIttype\fR. All result types are valid for these functions. The -\fBtst_range\fR global variable indicates the number of results that will be -reported for each call to one of these functions. It is initialized by the -library to 1, but may be set to a value > 1 by the test. Each call to one of -these functions will result in \fBtst_range\fR test case results being -reported, each with an identical message (\fItmesg\fR). \fBtst_range\fR is -always reset to 1 by the library before returning to the caller. -.P -\fBtst_brk()\fR and \fBtst_brkm()\fR are used to report results for all test -cases remaining in the test, and then call a cleanup function. The only -result types that are valid for these functions are: \fBTFAIL, TBROK, -and TCONF\fR. When called with a \fIttype\fR of \fBTFAIL\fR or -\fBTBROK\fR, one result of the specified \fIttype\fR will be printed, -followed by results of type \fBTBROK\fR for the remaining test cases. When -called with a \fIttype\fR of \fBTCONF\fR, the specified -\fIttype\fR will be printed for all remaining test cases. If \fIfunc\fR is -not NULL, \fBtst_brk()\fR and \fBtst_brkm()\fR will call \fIfunc\fR after all -results have been printed. If the call to \fIfunc\fR returns, -\fBtst_brk()\fR and \fBtst_brkm()\fR will then call \fBtst_exit()\fR. If -\fIfunc\fR is NULL, \fBtst_brk()\fR and \fBtst_brkm()\fR return to the caller -after all results have been printed. If \fBtst_brk()\fR is called with a -\fIfname\fR argument, the contents of the file will only be printed for the -first reported result. \fBtst_brk()\fR takes the \fIfname\fR argument -whereas \fBtst_brkm()\fR does not. -.P -\fBtst_old_flush()\fR is used to print any results pending because of -\fBCONDENSE\fR or \fBNOPASS\fR modes (described below), and flushes the -output stream. -.P -\fBtst_exit()\fR is used to allow tests to exit with a meaningful exit -value. A bit is set in the exit value for each of the non passing test -case result types (TFAIL, TBROK, and TWARN) encountered by the library. -Thus any bit which is set in the exit value indicates that the -corresponding result flag was used at least once in the test run. -.P -The current bit fields for the result types are as follows: -.RS 5 -.TP 10 -TPASS -0000 /* .... .... */ -.TP 10 -TFAIL -0001 /* .... ...1 */ -.TP 10 -TBROK -0002 /* .... ..1. */ -.TP 10 -TWARN -0004 /* .... .1.. */ -.TP 10 -TINFO -0020 /* ...1 .... */ -.TP 10 -TCONF -0040 /* ..1. .... */ -.RE -.P -NOTE: \fBTPASS and TINFO\fR do not have an effect -on the test program exit status. -.P -\fBtst_environ()\fR is used to ensure that results reported by this library -will go to the original stdout, even if the test changes the original stdout -to another file, or closes it. A test may want to do this in order to -redirect output that normally goes to stdout (e.g. printf() output) to a -file. \fBtst_environ()\fR returns 0 upon successful completion, and -1 if it -encountered any problems. -.SS Output Modes -Four output display modes are supported by the \fBtst_resm()\fR family of -functions to enhance output readability. The active mode is controlled via -the environment variable \fBTOUTPUT\fR, which must be set prior to the start -of the test in order to have any effect (see \fBksh\fR(1) for information on -environment variables). The supported modes are as follows: -.RS 5 -.TP 15 -.B VERBOSE -A test result output line is generated for each test result. This is the -default mode. -.TP 15 -.B CONDENSE -Consecutive, identical PASS, FAIL, BROK, CONF, and RETR test results are -condensed into one output line. The test case number field contains the range -of results involved. WARN and INFO output lines are not condensed, but -printed as usual. -.TP 15 -.B NOPASS -All PASS, CONF, INFO, and RETR output lines are discarded (i.e. not printed), -and consecutive, identical FAIL and BROK output lines are condensed as in -\fBCONDENSE\fR mode. WARN output lines are printed as usual. -.TP 15 -.B DISCARD -All output lines are discarded. -.RE -.SH EXAMPLES -.nf -#include "test.h" - -char *TCID = "tsttcs01"; /* set test case identifier */ -int TST_TOTAL = 15; /* set total number of test results */ - -main() -{ - . - . - /* a successful test result */ - tst_resm(TPASS, "\fIwhat was tested\fR"); - . - . - - /* break all remaining test results */ - tst_brkm(TBROK, cleanup, "\fIwhat didn't work\fR"); - /* or */ - tst_brk(TBROK, file, cleanup, "\fIwhat didn't work\fR"); - . - . - - /* exit after all test results have been passed to tst_res */ - tst_exit(); -} -.fi -.P -Sample output: -.RS 5 -.nf -tsttcs01 1 PASS : Able to create MAXUP processes -tsttcs01 2 FAIL : Too many processes (MAXUP+1) created -tsttcs01 3 BROK : tabinfo(PROCTAB, &tbs) failed; errno = 13: Permission denied -.fi -.SH "SEE ALSO" -tst_setup(1), -printf(3C), -ksh(1). -.SH DIAGNOSTICS -.P -A WARN result message will be printed if any of the following occur: -.RS 5 -.P -If an invalid test type is specified. -.P -If \fBtst_count\fR is negative. -.P -If one of the \fBtst_brk[m]()\fR routines is called with a test type -other than \fBTFAIL, TBROK, TCONF\fR. -.P -If there are any problems opening/reading/writing the contents of \fIfname\fR. -.RE -.SH LIMITATIONS -If \fIfname\fR is NULL and \fItmesg\fR is NULL or empty, the result message -will be empty. This allows a test to not print a message for a result, but -it is not advised. -.SH NOTES -In multithreaded environment, output of \fBtst_resm_hexd()\fR may be interleaved -with messages produced by other threads. -.SH BUGS -.P -The programmer is free to alter the value of \fBtst_count\fR causing possible -test result order problems. diff --git a/doc/man3/tst_sig.3 b/doc/man3/tst_sig.3 deleted file mode 100755 index 7550644f..00000000 --- a/doc/man3/tst_sig.3 +++ /dev/null @@ -1,141 +0,0 @@ -.\" -.\" $Id: tst_sig.3,v 1.1 2000/07/27 16:59:03 alaffin Exp $ -.\" -.\" 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/ -.\" -.TH TST_SIG 3 07/25/2000 "Linux Test Project" -.SH NAME -tst_sig \- set up for unexpected signals -.SH SYNOPSIS -.nf -\fB -#include "test.h" - -void tst_sig(fork_flag, handler, cleanup) -char *fork_flag; -int (*handler)(); -void (*cleanup)(); -\fR -.fi -.SH DESCRIPTION -.P -\fItst_sig\fR is used by UNICOS test case programs -to set up signal handling functions for unexpected -signals. This provides test cases with a graceful means -of exiting following an unexpected interruption by a signal. -\fItst_sig\fR should be called only once by a test -program. -.P -The \fIfork_flag\fR parameter is used to tell \fItst_sig\fR -whether or not to ignore the SIGCHLD signal caused by the death of a -child process that had previously been created by the -\fIfork\fR(2) system call (see \fIsignal\fR(2) for more -information on the SIGCHLD signal). -.P -Setting \fIfork_flag\fR to FORK will cause \fItst_sig\fR to -ignore the SIGCHLD signal. This option should be set if the -test program directly (eg. call \fIfork\fR(2)) or indirectly -(eg. call \fIsystem\fR(3S)) creates a child process. -.P -Setting \fIfork_flag\fR to NOFORK will cause \fItst_sig\fR to -treat the SIGCHLD signal just as any other unexpected -signal (ie. the \fIhandler\fR will be called). -This option should be set by any test program which does not -directly or indirectly create any child processes. -.P -The \fIhandler\fR parameter is -a pointer to a function returning type int which is -executed upon the receipt of an unexpected signal. -The test program may pass a pointer to a signal handling -function or it may elect to use a \fIdefault handler\fR -supplied by \fItst_sig\fR. - -The \fIdefault handler\fR is specified by passing DEF_HANDLER -as the \fIhandler\fR argument. Upon receipt of an unexpected -signal, the \fIdefault handler\fR will generate -\fItst_res\fR(3) messages for all test results that had -not been completed at the time of the signal, execute the -\fIcleanup\fR routine, if provided, and call \fItst_exit\fR. -Note: if the \fIdefault handler\fR is used, the variables -\fBTCID\fR and \fBtst_count\fR must be defined and available to -\fItst_sig\fR (see \fItst_res\fR(3)). -.P -The \fIcleanup\fR parameter is a pointer to a user-defined -function returning type void which is executed -by the \fIdefault handler\fR. The \fIcleanup\fR function -should remove any files, directories, processes, etc. created -by the test program. -If no cleanup is required, this parameter should be set to NULL. - -.SH EXAMPLES - -.nf -#include "test.h" - -/* - * the TCID and TST_TOTAL variables must be available to tst_sig - * if the \fIdefault handler\fR is used. The \fIdefault handler\fR will call - * \fItst_res\fR(3) and will need this information. - */ -int TCID = "tsttcs01"; /* set test case identifier */ -int TST_TOTAL = 5; /* set total number of test results */ - - - void tst_sig(); - - /* - * set up for unexpected signals: - * no \fIfork\fR() system calls will be executed during the test run - * use the default signal handler provided by \fItst_sig\fR - * no cleanup is necessary - */ - tst_sig(NOFORK, DEF_HANDLER, NULL); - - - void tst_sig(), cleanup(); - int handler(); - - /* - * set up for unexpected signals: - * \fIfork\fR() system calls will be executed during the test run - * use user-defined signal handler - * use cleanup - */ - tst_sig(FORK, handler, cleanup); - -.fi -.SH "SEE ALSO" -signal(2), -tst_setup(1). -.SH DIAGNOSTICS -.P -\fItst_sig\fR will output warnings in standard \fItst_res\fR -format if it cannot set up the signal handlers. diff --git a/doc/man3/tst_tmpdir.3 b/doc/man3/tst_tmpdir.3 deleted file mode 100755 index b8a8d799..00000000 --- a/doc/man3/tst_tmpdir.3 +++ /dev/null @@ -1,76 +0,0 @@ -.\" -.\" $Id: tst_tmpdir.3,v 1.1 2000/07/27 16:59:03 alaffin Exp $ -.\" -.\" 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/ -.\" -.TH TST_TMPDIR 3 07/25/2000 "Linux Test Project" -.SH NAME -tst_tmpdir \- create a unique testing directory and make it current. -.br -tst_rmdir \- remove the directory created by \fBtst_tmpdir\fR. -.SH SYNOPSIS -\fBvoid tst_tmpdir() -.P -void tst_rmdir() -.P -extern char *TESTDIR;\fR -.SH DESCRIPTION -The \fBtst_tmpdir()\fR function uses the first three characters of the -\fBTCID\fR global variable as the prefix in forming a unique directory name -(via \fBtempnam\fR(3S)). The directory is then created and made the current -working directory. -.P -If \fBtst_tmpdir()\fR cannot form a unique directory name, create the -directory, or \fBchdir\fR to the directory, it uses \fBtst_brk()\fR to issue -"BROK" messages for all test cases. It then exits via \fBtst_exit()\fR. -Because \fBtst_tmpdir()\fR exits in the event of a problem, a test must call -it \fBbefore\fR performing any operations that would require running a -cleanup routine. -.P -The \fBtst_rmdir()\fR function recursively removes the directory created by -\fBtst_tmpdir()\fR. This function should be used \fBonly\fR as a companion -to \fBtst_tmpdir()\fR and should be called immediately prior to the test -exiting via \fBtst_exit()\fR. -.P -\fBtst_rmdir()\fR uses the \fBsystem\fR(3S) library routine (which in turn -calls \fBfork\fR(2)), so tests that use it \fBcannot\fR treat SIGCHLD as an -unexpected signal. -.P -Users may gain access to the name of the temporary directory by declaring the -external character pointer \fBTESTDIR\fR. -.SH DIAGNOSTICS -The \fBtst_rmdir()\fR function will check the \fBTESTDIR\fR global variable -to ensure that the user is not attempting to remove the root directory or -some unspecified directories with a "*" parameter. All error/warning -messages are delivered through \fBtst_resm()\fR. -.SH "SEE ALSO" -fork(2), system(3S), tst_res(3), tmpnam(3S). - diff --git a/doc/man3/usctest.3 b/doc/man3/usctest.3 deleted file mode 100755 index df3c5ad1..00000000 --- a/doc/man3/usctest.3 +++ /dev/null @@ -1,164 +0,0 @@ -.\" $Id: usctest.3,v 1.2 2000/08/31 18:40:28 nstraz Exp $ -.\" -.\" 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/ -.\" -.TH USCTEST 3 01/21/2011 "Linux Test Project" -.SH NAME -usctest \- macros and libraries for common functions in system call tests -.SH SYNOPSIS -\fBRoutines:\fR -.br -.in +1 -char *\fBparse_opts(\fI...\fB)\fR -.in -1 -.sp -\fBMacros\fR -.in +1 -.br -\fBTEST_PAUSE\fR -.br -\fBTEST(\fIsyscall\fB)\fR -.br -.\"\fBTEST_CALLER(\fIsyscall\fB, \fIpid\fB)\fR -.\".br -\fBTEST_VOID(\fIsyscall\fB)\fR -.br -\fBTEST_CLEANUP\fR -.br -\fBTEST_LOOPING(\fIcounter\fB)\fR -.br -\fBTEST_ERROR_LOG(\fIerrno\fB)\fR -.br -\fBTEST_EXP_ENOS(\fIarray\fB)\fR -.in -1 -.sp -\fBGlobal Variable(s)\fR (see \fBparse_opts(3)\fR for complete list): -.br -.in +1 -int \fBTEST_RETURN\fR; /* set by the \fBTEST\fR macro to the return code from \fIsyscall\fR */ -.br -int \fBTEST_ERRNO\fR; /* set by the \fBTEST\fR macro to the value of \fBerrno\fR after \fIsyscall\fR returns */ -.br -/* All STD_* variables referenced below are set by the \fBparse_opts(3)\fR routine. */ -.in -1 - -.SH DESCRIPTION -The \fBTEST_PAUSE\fR macro checks if the global variable STD_PAUSE is set. If so, it -pauses for a SIGUSR1 before continuing execution. The signal handler used does nothing. -After the signal is processed, the previous action is replaced for SIGUSR1. -.sp -The \fBTEST(\fIsyscall\fB)\fR macro executes (\fIsyscall\fR) and times its execution. -It saves the max time, min time, accumulated time, and -execution count, if STD_TIMING_ON is set. -.sp -.\"The\fBTEST_CALLER(\fIsyscall\fB, \fIpid\fB)\fR macro executes (\fIsyscall\fR) and times its execution. -.\"It saves the max time, min time, accumulated time, and -.\"execution count, if STD_TIMING_ON is set and if \fIpid\fR is equal to the current pid. -.\".sp -The \fBTEST_VOID(\fIsyscall\fB)\fR macro works exactly the same as the \fBTEST()\fR -macro except that it does NOT set the global \fBTEST_RETURN\fR. It is intended -to be used with system calls that do not have a return value. -.sp -The \fBTEST_CLEANUP\fR macro prints timing statistics, -accumulated through the TEST macro, if STD_TIMING_ON is set. Also, prints the \fBerrno\fR return -counts as logged by the \fBTEST_ERROR_LOG\fR macro, if STD_ERR_LOG is set. \fBTEST_CLEANUP\fR uses -\fBtst_resm(3)\fR to output this information. -.sp -The \fBTEST_LOOPING(\fIcounter\fB)\fR macro checks \fIcounter\fR against -the global variable STD_LOOP_COUNTER. If \fIcounter\fR is less than STD_LOOP_COUNTER or STD_INFINITE -is set, it returns TRUE. -.sp -The \fBTEST_ERROR_LOG\fR macro records the return of \fIerrno\fR as unexpected, unless the option to -turn it off is specified on the command line. -.sp -The \fBTEST_EXP_ENOS(\fIarray\fB)\fR macro sets an internal flag for each errno in \fIarray\fR, indicating -that the errno is expected at some point in the test. This is used by the TEST_CLEANUP macro to determine -which errnos are expected when printing the log. The \fIarray\fR must be zero terminated. -.sp -The \fBparse_opts\fR routine parses the command line (see \fBparse_opts(3)\fR). All STD_* global -variables used are set by the \fBparse_opts(3)\fR routine. - -.SH EXAMPLES -Below is a partial template of a system call test using these routines, macros, and global variables. - -.nf -void setup(void) -{ - TEST_PAUSE; /* Pause if option specified */ -} - -void cleanup(void) -{ - TEST_CLEANUP; -} - -int main(int argc, char *argv[]) -{ - int lc; - char *msg; - - int exp_enos[]={EACCESS, 0}; - - - TEST_EXP_ENOS(exp_enos); /* set expected errnos */ - - setup(); /* execute setup */ - - /* parse options */ - msg = parse_opts(ac, av, NULL, NULL); - - /* Check parse_opts return */ - - for (lc=0; TEST_LOOPING(lc); lc++) { - TEST(open("file", O_RDWR)) - - if (TEST_RETURN == -1) { - TEST_ERROR_LOG(TEST_ERRNO) - /* BREAK test case, or whatever... */ - } - - } - - cleanup(); - tst_exit(); -} -.fi - -.SH "SEE ALSO" -parse_opts(3). - -.SH "RETURN VALUES" -The TEST_LOOPING macro evaluates to TRUE (1) or FALSE (0), and is intended for -use in while or for loops. The TEST macro places the return value from -\fIsyscall\fR in the global variable TEST_RETURN and the errno in the global -variable TEST_ERRNO. The \fBTEST_PAUSE\fR, \fBTEST_CLEANUP\fR, -\fBTEST_ERROR_LOG\fR, and \fBTEST_EXP_ENOS\fR macros do not have any return -values. diff --git a/doc/nommu-notes.txt b/doc/nommu-notes.txt deleted file mode 100755 index 4baeff3b..00000000 --- a/doc/nommu-notes.txt +++ /dev/null @@ -1,171 +0,0 @@ -------------- ---- Intro --- -------------- - -Linux running on processors without a memory management unit place certain -restrictions on the userspace programs. Here we will provide some guidelines -for people who are not familiar with such systems. - -If you are not familiar with virtual memory, you might want to review some -background such as: - http://en.wikipedia.org/wiki/Virtual_Memory - /usr/src/linux/Documentation/nommu-mmap.txt - ----------------------------- ---- No memory protection --- ----------------------------- - -By virtue of every process getting its own virtual memory space, applications -are protected from each other. So a bad memory access in one will not affect -the memory of another. When processors forgo virtual memory, they typically -do not add memory protection back in to the hardware. There are one or two -exceptions to this rule, but for now, we'll assume no one supports it. - -In practical terms, this means you cannot dereference bad pointers directly -and expect the kernel to catch and kill your application. However, you can -expect the kernel to catch some bad pointers when given to system calls. - -For example, this will "work" in the sense that no signal will be sent: - char *foo = NULL; - foo[0] = 'a'; - foo[1] = 'b'; - -However, the kernel should return errors when using "standard" bad pointers -with system calls. Such as: - char *foo = NULL; - write(1, foo, 10); - -> kernel will return EFAULT or similar -The other bad pointer you can rely on in your tests is -1: - char *foo = (void *)-1; - read(0, foo, 10); - -> kernel will return EFAULT or similar - -Otherwise, no bad pointer may reliably be tested, either directly or -indirectly via the kernel. This tends to be a large part of the UCLINUX -ifdef code that shows up in LTP. - ----------------- ---- No forks --- ----------------- - -The ubiquitous fork() function relies completely on the Copy On Write (COW) -functionality provided by virtual memory to share pages between processes. -Since this isn't feasible without virtual memory, there is no fork() function. -You will either get a linker error (undefined reference to fork) or you will -get a runtime failure of ENOSYS. - -Typically, fork() is used for very few programming paradigms: - - daemonization - - run a program - - parallelism - -For the daemonization functionality, simply use the daemon() function. This -works under both MMU and NOMMU systems. - -To run a program, simply use vfork() followed by an exec-style function. -And change the error handler in the child from exit() to _exit(). This too -works under both MMU and NOMMU systems. But be aware of vfork() semantics -- -since the parent and child share the same memory process, the child has to be -careful in what it does. This is why the recommended construct is simply: - pid_t child = vfork(); - if (vfork == 0) - _exit(execl(....)); - -For parallelism where processes use IPC to work together, you have to options, -neither of which are easy. You can rewrite to use threads, or you can re-exec -yourself with special flags to pass along updated runtime state. This is what -the self_exec() helper function in LTP is designed for. - -------------------------- ---- No overcommitting --- -------------------------- - -Virtual memory allows people to do malloc(128MiB) and get back a buffer that -big. But that buffer is only of virtual memory, not physical. On a NOMMU -system, the memory comes immediately from physical memory and takes it away -from anyone else. - -Avoid large mallocs. - ---------------------- ---- Fragmentation --- ---------------------- - -On a MMU system, when physical memory gets fragmented, things slow down. But -they keep working. This is because every new process gets a clean virtual -memory address space. While processes can fragment their own virtual address -space, this usually takes quite a long time and a lot of effort, so generally -it is not a problem people hit. - -On a NOMMU system, when physical memory gets fragmented, access to large -contiguous blocks becomes unavailable which means requests fail. Even if your -system has 40MiB _total_ free, the largest contiguous block might only be 1MiB -which means that allocations larger than that will always fail. - -Break up your large memory allocations when possible. Generally speaking, -single allocations under 2MiB aren't a problem. - ------------------ ---- No paging --- ------------------ - -No virtual memory means you can't mmap() a file and only have the pages read in -(paged) on the fly. So if you use mmap() on a file, the kernel must allocate -memory for it and read in all the contents immediately. - ---------------------- ---- No swap space --- ---------------------- - -See the "No paging" section above. For the same reason, there is no support -for swap partitions. Plus, nommu typically means embedded which means flash -based storage which means limited storage space and limited number of times -you can write it. - -------------------------- ---- No dynamic stacks --- -------------------------- - -No virtual memory means that applications can't all have their stacks at the -top of memory and allowed to grown "indefinitely" downwards. Stack space is -fixed at process creation time (when it is first executed) and cannot grow. -While the fixed size may be increased, it's best to avoid stack pressure in -the first place. - -Avoid the alloca() function and use malloc()/free() instead. - -Avoid declaring large buffers on the stack. Some people like to do things -such as: - char buf[PATH_MAX]; -This will most likely smash the stack on nommu systems ! Use global variables -(the bss), or use malloc()/free() type functions. - -------------------------------- ---- No dynamic data segment --- -------------------------------- - -No virtual memory means that mappings cannot arbitrarily be extended. Another -process might have its own mapping right after yours! This is where the brk() -and sbrk() functions come into play. These are most often used to dynamically -increase the heap space via the C library, but a few people use these manually. - -Best if you simply avoid them, and if you're writing tests to exercise these -functions specifically, make them nops/XFAIL for nommu systems. - -------------------------------- ---- Limited shared mappings --- -------------------------------- - -No virtual memory means files cannot be mmapped in and have writes to it -written back out to disk on the fly. So you cannot use MAP_SHARED when -mmapping a file. - -------------------------- ---- No fixed mappings --- -------------------------- - -The MAP_FIXED option to mmap() is not supported. It doesn't even really work -all that well under MMU systems. - -Best if you simply avoid it, and if you're writing tests to exercise this -option specifically, make them nops/XFAIL for nommu systems. diff --git a/doc/C-Test-API.asciidoc b/doc/old/C-Test-API.asciidoc similarity index 95% rename from doc/C-Test-API.asciidoc rename to doc/old/C-Test-API.asciidoc index dab81156..72fd2731 100644 --- a/doc/C-Test-API.asciidoc +++ b/doc/old/C-Test-API.asciidoc @@ -17,7 +17,6 @@ Let's start with an example, following code is a simple test for a 'getenv()'. [source,c] ------------------------------------------------------------------------------- /*\ - * [Description] * Tests basic functionality of getenv(). * * - create an env variable and verify that getenv() can get it @@ -94,26 +93,32 @@ in range of [0, '.tcnt' - 1]. IMPORTANT: Only one of '.test' and '.test_all' can be set at a time. -Each test has a limit on how long it can run and the limit composes of two -parts max_runtime and timeout. The max_runtime is a limit for how long can the -'.test_all' or a set of '.test' functions take and the timeout is static part -that should cover the duration of test setup and cleanup plus some safety. +Each test has a limit on how long it can run, composed of two parts: 'runtime' +and 'timeout'. The 'runtime' is the limit for how long the '.test_all' or a set +of '.test' main functions can execute. The 'timeout', on the other hand, applies +to the total time for setup, single test function invocation, cleanup, and some +additional safety margin. If test without an explicit 'runtime', the 'timeout' +governs the entire test duration. + +The timeout timer is also reset on each subsequent iteration with the test -i +parameter, variants or .all_filesystems. Any test that runs for more than a second or two has to make sure to: -- set the runtime either by setting the '.max_runtime' in tst_test or by - calling 'tst_set_max_runtime()' in the test setup +- set the runtime by setting '.runtime' in tst_test and calling 'tst_set_runtime()' + to monitor the remaining runtime and ensure the test exits when the runtime + limit is reached. -- monitor remaning runtime by regular calls to 'tst_remaining_runtime()' and - exit when runtime has been used up +- set the timeout by setting '.timeout' in tst_test to limit the whole test + run that does not use 'tst_remaining_runtime()'. -Test is free to exit before max_runtime has been used up for example when +Test is free to exit before runtime has been used up for example when minimal number of iteration was finished. The limit is applied to a single call of the '.test_all' function that means that for example when '.test_variants' or '.all_filesystems' is set the whole -test will be limited by 'variants * (max_runtime + timeout)' seconds and the -test runtime will be likely close to 'variants * max_runtime' seconds. +test will be limited by 'variants * (runtime + timeout)' seconds and the +test runtime will be likely close to 'variants * runtime' seconds. [source,c] ------------------------------------------------------------------------------- @@ -224,10 +229,12 @@ void tst_res(int ttype, char *arg_fmt, ...); Printf-like function to report test result, it's mostly used with ttype: |============================== -| 'TPASS' | Test has passed. -| 'TFAIL' | Test has failed. -| 'TINFO' | General message. -| 'TWARN' | Something went wrong but we decided to continue. Mostly used in cleanup functions. +| 'TPASS' | Test has passed. +| 'TFAIL' | Test has failed. +| 'TINFO' | General message. +| 'TDEBUG' | Debug message (new C API only, printed with '-D' or via 'LTP_ENABLE_DEBUG=1' or 'y' + environment variable), only for messages which would be too verbose for normal run. +| 'TWARN' | Something went wrong but we decided to continue. Mostly used in cleanup functions. |============================== The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print @@ -238,13 +245,13 @@ The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print void tst_brk(int ttype, char *arg_fmt, ...); ------------------------------------------------------------------------------- -Printf-like function to report error and exit the test, it can be used with ttype: +Printf-like function to report result and exits current test. If test uses +'.all_filesystems', '.test_variants' etc. the 'tst_brk()' exits current test +iteration e.g. currently running filesystem test or a test variant unless +'ttype' is set to 'TBROK'. -|============================================================ -| 'TBROK' | Something has failed in test preparation phase. -| 'TCONF' | Test is not appropriate for current configuration - (syscall not implemented, unsupported arch, ...) -|============================================================ +If 'ttype' is set to 'TBROK' all test processes are killed and the test exits +immediately with an error. The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print 'errno', 'TST_ERR' respectively. @@ -338,7 +345,7 @@ if (TST_RET > -1) { ------------------------------------------------------------------------------- The +TEST+ macro sets +TST_RET+ to its argument's return value and +TST_ERR+ to -+errno+. The +TTERNO+ flag can be used to print the error number's symbolic ++errno+. The +TTERRNO+ flag can be used to print the error number's symbolic value. No LTP library function or macro, except those in 'tst_test_macros.h', will @@ -399,13 +406,25 @@ WARNING: This function is not thread safe. [source,c] ------------------------------------------------------------------------------- -void tst_set_max_runtime(int max_runtime); +void tst_set_timeout(int timeout); ------------------------------------------------------------------------------- -Allows for setting max_runtime per test iteration dynamically in the test 'setup()', -the timeout is specified in seconds. There are a few testcases whose runtime -can vary arbitrarily, these can disable timeouts by setting it to -TST_UNLIMITED_RUNTIME. +Allows for setting the entire timeout dynamically during the test setup(). The +timeout is specified in seconds and represents the total time allowed for a single +test iteration, including setup, runtime, and teardown phases. + +[source,c] +------------------------------------------------------------------------------- +void tst_set_runtime(int runtime); +------------------------------------------------------------------------------- + +Allows for setting the runtime per test iteration dynamically during the test 'setup()'. +The runtime is specified in seconds and represents the duration the test is allowed +to execute its main workload, excluding setup and teardown phases. + +This function is useful for tests where the duration of the main workload can be +controlled or needs to be adjusted dynamically. For example, tests that loop until +the runtime expires can use this function to define how long they should run. [source,c] ------------------------------------------------------------------------------- @@ -628,15 +647,15 @@ IMPORTANT: You have to set the '.forks_child' flag in the test structure Results reported by 'tst_res()' are propagated to the parent test process via block of shared memory. -Calling 'tst_brk()' causes child process to exit with non-zero exit value. -Which means that it's safe to use 'SAFE_*()' macros in the child processes as -well. +Calling 'tst_brk()' causes child process to set the test library abort flag and +exits the test immediately. Which means that it's safe to use 'SAFE_*()' macros +in the child processes as well. Children that outlive the 'test()' function execution are waited for in the test library. Unclean child exit (killed by signal, non-zero exit value, etc.) -will cause the main test process to exit with 'tst_brk()', which especially -means that 'TBROK' propagated from a child process will cause the whole test -to exit with 'TBROK'. +will cause the main test process to exit with 'tst_brk()'. That means that all +test child processes are supposed to exit with success unless they are +explicitly waited for. If a test needs a child that segfaults or does anything else that cause it to exit uncleanly all you need to do is to wait for such children from the @@ -1332,8 +1351,9 @@ return value is '255' if 'execvp()' failed with 'ENOENT' and '254' otherwise. 'stdout_path' and 'stderr_path' determine where to redirect the program stdout and stderr I/O streams. -The 'SAFE_CMD()' macro can be used automatic handling non-zero exits (exits -with 'TBROK') and 'ENOENT' (exits with 'TCONF'). +'SAFE_CMD()' is a wrapper for 'tst_cmd()' which can be used for automatic +handling non-zero exit (exits with 'TBROK') and 'ENOENT' (the program not in +'$PATH', exits with 'TCONF'). .Example [source,c] @@ -1631,7 +1651,7 @@ with 'TBROK'. This behavior can be changed using tst_path_val.flags: * 'TST_SR_TBROK_RO' – End test with 'TBROK' if the file is read-only * 'TST_SR_TCONF_RO' – End test with 'TCONF' if the file is read-only * 'TST_SR_SKIP_RO' – Continue without saving the file if it is read-only -* 'TST_SR_IGNORE_ERR' – Ignore errors when writing new value into the file +* 'TST_SR_IGNORE_ERR' – Ignore all errors during reading and writing the file Common flag combinations also have shortcuts: @@ -1812,7 +1832,7 @@ fault or EFAULT depending on if the access happened in userspace or the kernel respectively. The canary before the buffer will also catch any write access outside of the buffer. -The purpose of the patch is to catch off-by-one bugs which happens when +The purpose of this feature is to catch off-by-one bugs which happens when buffers and structures are passed to syscalls. New tests should allocate guarded buffers for all data passed to the tested syscall which are passed by a pointer. @@ -2016,9 +2036,8 @@ static struct tst_test test = { -------------------------------------------------------------------------------- Above is a minimal template for a test using fuzzy-sync. In a simple case, you -just need to put the bits you want to race inbetween 'start_race' and -'end_race'. Meanwhile, any setup you need to do per-iteration goes outside the -windows. +just need to put the bits you want to race between 'start_race' and 'end_race'. +Meanwhile, any setup you need to do per-iteration goes outside the windows. Fuzzy sync synchronises 'run_a' and 'run_b', which act as barriers, so that neither thread can progress until the other has caught up with it. There is @@ -2336,7 +2355,7 @@ Some tests require at least size(MB) of free RAM or Swap. To make sure that test will run only on systems with more than minimal required amount of RAM set `.min_mem_avail = N`. -Similarily for tests that require certain amount of free Swap use +Similarly for tests that require certain amount of free Swap use `.min_swap_avail = N`. 1.40 Test tags @@ -2424,6 +2443,25 @@ Test can be skipped on various conditions: on enabled SecureBoot ('.skip_in_secureboot = 1'), lockdown ('.skip_in_lockdown = 1') or in 32-bit compat mode ('.skip_in_compat = 1'). +1.43 Set resource limits +~~~~~~~~~~~~~~~~~~~~~~~~ + +'.ulimit' allows to set resource limits on particular resource. NOTE: It sets 'rlim_max' +only if it's higher than 'rlim_cur'. + +[source,c] +------------------------------------------------------------------------------- +#include "tst_test.h" + +static struct tst_test test = { + ... + .ulimit = (const struct tst_ulimit_val[]) { + {RLIMIT_STACK, RLIM_INFINITY}, + {} + }, +}; +------------------------------------------------------------------------------- + 2. Common problems ------------------ diff --git a/doc/C-Test-Network-API.asciidoc b/doc/old/C-Test-Network-API.asciidoc similarity index 67% rename from doc/C-Test-Network-API.asciidoc rename to doc/old/C-Test-Network-API.asciidoc index 3bf2a1f8..feedd5a9 100644 --- a/doc/C-Test-Network-API.asciidoc +++ b/doc/old/C-Test-Network-API.asciidoc @@ -143,7 +143,7 @@ static void setup(void) When opening a localhost socket isn't enough and the test needs special device or routing configuration, the netdevice library can create the required network setup without calling external programs. Internally, the netdevice functions -use a rtnetlink socket to communicate with the kernel. +use a netlink socket to communicate with the kernel. All of these functions will call +tst_brk()+ on failure, unless stated otherwise. Error values described below are returned only during test cleanup @@ -274,12 +274,12 @@ static void setup(void) } ------------------------------------------------------------------------------- -3 rtnetlink API ---------------- +3 Netlink API +------------- -+#include "tst_rtnetlink.h"+ ++#include "tst_netlink.h"+ -The rtnetlink library provides helper functions for constructing and sending +The netlink library provides helper functions for constructing and sending arbitrary messages and parsing kernel responses. All of the functions below will call +tst_brk()+ on failure, unless stated @@ -291,16 +291,16 @@ stage. [source,c] ------------------------------------------------------------------------------- -struct tst_rtnl_context; +struct tst_netlink_context; -struct tst_rtnl_attr_list { +struct tst_netlink_attr_list { unsigned short type; const void *data; ssize_t len; - const struct tst_rtnl_attr_list *sublist; + const struct tst_netlink_attr_list *sublist; }; -struct tst_rtnl_message { +struct tst_netlink_message { struct nlmsghdr *header; struct nlmsgerr *err; void *payload; @@ -308,17 +308,18 @@ struct tst_rtnl_message { }; ------------------------------------------------------------------------------- -+struct tst_rtnl_context+ is an opaque rtnetlink socket with buffer for ++struct tst_netlink_context+ is an opaque netlink socket with buffer for constructing and sending arbitrary messages using the functions described -below. Create a new context using +RTNL_CREATE_CONTEXT()+, then free it using -+RTNL_DESTROY_CONTEXT()+ when you're done with it. +below. Create a new context using +NETLINK_CREATE_CONTEXT()+, then free it +using +NETLINK_DESTROY_CONTEXT()+ when you're done with it. -+struct tst_rtnl_attr_list+ is a helper structure for defining complex ++struct tst_netlink_attr_list+ is a helper structure for defining complex rtnetlink message attribute payloads, including nested attribute lists. Every list and sublist defined using this structure is terminated by item with negative +len+. -- +type+ is the attribute type that will be stored in +struct rtattr.rta_type+. +- +type+ is the attribute type that will be stored in +struct nlattr.nla_type+ + or +struct rtattr.rta_type+. - +data+ contains arbitrary attribute payload. @@ -326,15 +327,15 @@ negative +len+. set +len+ to 0. The last item in a list or sublist must have negative length. - +sublist+ contains a nested attribute list which will be appended after - +data+ as part of the attribute payload. +struct rtattr.rta_len+ will be - calculated automatically with proper alignment, do _not_ add the sublist size - to the +len+ field. If you do not want to add nested attributes, set - +sublist+ to +NULL+. + +data+ as part of the attribute payload. +struct nlattr.nla_len+ or + +struct rtattr.rta_len+ will be calculated automatically with proper + alignment, do _not_ add the sublist size to the +len+ field. If you do not + want to add nested attributes, set +sublist+ to +NULL+. -+struct tst_rtnl_message+ is a structure holding partially parsed rtnetlink -messages received from the kernel. +RTNL_RECV()+ returns an array of these ++struct tst_netlink_message+ is a structure holding partially parsed netlink +messages received from the kernel. +NETLINK_RECV()+ returns an array of these structures with the last item having +NULL+ in the +header+ field. Call -+RTNL_FREE_MESSAGE()+ to free a message list returned by +RTNL_RECV()+. ++NETLINK_FREE_MESSAGE()+ to free a message list returned by +NETLINK_RECV()+. - +header+ is the netlink header structure of the message. +NULL+ in the header field terminates a list of messages. @@ -349,84 +350,110 @@ structures with the last item having +NULL+ in the +header+ field. Call 3.2 Sending and receiving messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- +struct tst_rtnl_context *RTNL_CREATE_CONTEXT(void)+ – Creates a new - rtnetlink communication context for use with the functions described below. - Returns +NULL+ on error. +- +struct tst_netlink_context *NETLINK_CREATE_CONTEXT(int protocol)+ – Creates + a new netlink communication context with given netlink protocol for use + with the functions described below. Returns +NULL+ on error. -- +void RTNL_FREE_MESSAGE(struct tst_rtnl_message *msg)+ – Frees an array of - messages returned by +RTNL_RECV()+. +- +void NETLINK_FREE_MESSAGE(struct tst_netlink_message *msg)+ – Frees + an array of messages returned by +NETLINK_RECV()+. -- +void RTNL_DESTROY_CONTEXT(struct tst_rtnl_context *ctx)+ – Closes a - communication context created by +RTNL_CREATE_CONTEXT()+. +- +void NETLINK_DESTROY_CONTEXT(struct tst_netlink_context *ctx)+ – Closes a + communication context created by +NETLINK_CREATE_CONTEXT()+. -- +int RTNL_SEND(struct tst_rtnl_context *ctx)+ – Sends all messages waiting - in +ctx+ buffer to the kernel. If there are multiple messages to send, a new - +NLMSG_DONE+ message will be added automatically. Returns the number of - bytes sent on success. Return 0 or negative value on error. +- +int NETLINK_SEND(struct tst_netlink_context *ctx)+ – Sends all messages + waiting in +ctx+ buffer to the kernel. If there are multiple messages + to send, a new +NLMSG_DONE+ message will be added automatically. Returns + the number of bytes sent on success. Return 0 or negative value on error. -- +int RTNL_SEND_VALIDATE(struct tst_rtnl_context *ctx)+ – Sends all messages - just like +RTNL_SEND()+, then receives the response from the kernel and - validates results of requests sent with the +NLM_F_ACK+ flag. This function - calls +tst_brk()+ as usual if communication fails but it will return error - status without terminating the test if one of the received messages contains - error code. See +RTNL_CHECK_ACKS()+ below for explanation of the return - value. +- +int NETLINK_SEND_VALIDATE(struct tst_netlink_context *ctx)+ – Sends all + messages just like +NETLINK_SEND()+, then receives the response from + the kernel and validates results of requests sent with the +NLM_F_ACK+ flag. + This function calls +tst_brk()+ as usual if communication fails but it will + return error status without terminating the test if one of the received + messages contains error code. See +NETLINK_CHECK_ACKS()+ below for + explanation of the return value. -- +int RTNL_WAIT(struct tst_rtnl_context *ctx)+ – Waits until data becomes - available to read from the rtnetlink socket (timeout: 1 second). Returns 1 +- +int NETLINK_WAIT(struct tst_netlink_context *ctx)+ – Waits until data becomes + available to read from the netlink socket (timeout: 1 second). Returns 1 if there is data to read, 0 on timeout or -1 on error. -- +struct tst_rtnl_message *RTNL_RECV(struct tst_rtnl_context *ctx)+ – Receives - rtnetlink messages from the kernel. The messages are received in non-blocking - mode so calling +RTNL_WAIT()+ first is recommended. Returns an array of - partially parsed messages terminated by an item with +NULL+ in the +header+ - field. On error or when there are no messages to receive, returns +NULL+. - Call +RTNL_FREE_MESSAGE()+ to free the returned data. +- +struct tst_netlink_message *NETLINK_RECV(struct tst_netlink_context *ctx)+ – + Receives netlink messages from the kernel. The messages are received + in non-blocking mode so calling +NETLINK_WAIT()+ first is recommended. + Returns an array of partially parsed messages terminated by an item with + +NULL+ in the +header+ field. On error or when there are no messages + to receive, returns +NULL+. Call +NETLINK_FREE_MESSAGE()+ to free + the returned data. -- +int RTNL_CHECK_ACKS(struct tst_rtnl_context *ctx, struct tst_rtnl_message - *response)+ – Validate results of requests sent with the +NLM_F_ACK+ flag. - Do not call +RTNL_ADD_MESSAGE()+ between +RTNL_SEND()+ and - +RTNL_CHECK_ACKS()+ because it will reset the state of +ctx+ and prevent - result validation. Returns 1 if all messages sent with the +NLM_F_ACK+ flag - have a corresponding message in +response+ and the error code is 0. If any - of the expected response messages is missing, this function will call - +tst_brk()+ (or return 0 during test cleanup phase). If any of the response - messages has non-zero error code, this function will return 0 and store the - first non-zero error code in global variable +tst_rtnl_errno+ (sign-flipped - just like regular libc +errno+). +- +int NETLINK_CHECK_ACKS(struct tst_netlink_context *ctx, + struct tst_netlink_message *response)+ – Validate results of requests sent + with the +NLM_F_ACK+ flag. Do not call +NETLINK_ADD_MESSAGE()+ between + +NETLINK_SEND()+ and +NETLINK_CHECK_ACKS()+ because it will reset the state + of +ctx+ and prevent result validation. Returns 1 if all messages sent + with the +NLM_F_ACK+ flag have a corresponding message in +response+ and + the error code is 0. If any of the expected response messages is missing, + this function will call +tst_brk()+ (or return 0 during test cleanup phase). + If any of the response messages has non-zero error code, this function will + return 0 and store the first non-zero error code in global variable + +tst_netlink_errno+ (sign-flipped just like regular libc +errno+). 3.3 Creating messages ~~~~~~~~~~~~~~~~~~~~~ -- +int RTNL_ADD_MESSAGE(struct tst_rtnl_context *ctx, const struct nlmsghdr - *header, const void *payload, size_t payload_size)+ – Adds new rtnetlink - message to +ctx+ buffer. You need to provide message +header+ and optional - +payload+. +payload_size+ is the size of +payload+ data in bytes. If you - don't want to add any payload data, set +payload+ to +NULL+ and +- +int NETLINK_ADD_MESSAGE(struct tst_netlink_context *ctx, const struct + nlmsghdr *header, const void *payload, size_t payload_size)+ – Adds new + netlink message to +ctx+ buffer. You need to provide message +header+ and + optional +payload+. +payload_size+ is the size of +payload+ data in bytes. + If you don't want to add any payload data, set +payload+ to +NULL+ and +payload_size+ to 0. This function will automatically fill the +nlmsg_len+, +nlmsg_seq+ and +nlmsg_pid+ fields of the new message header. You don't need to set those. It'll also automatically add +NLM_F_MULTI+ flag when needed. Returns 1 on success, 0 on error. Note that the first call of - +RTNL_ADD_MESSAGE()+ after +RTNL_SEND()+ will reset the state of +ctx+ - and +RTNL_CHECK_ACKS()+ will not work correctly until the next +RTNL_SEND()+. + +NETLINK_ADD_MESSAGE()+ after +NETLINK_SEND()+ will reset the state of +ctx+ + and +NETLINK_CHECK_ACKS()+ will not work correctly until the next + +NETLINK_SEND()+. -- +int RTNL_ADD_ATTR(struct tst_rtnl_context *ctx, unsigned short type, const - void *data, unsigned short len)+ – Adds new attribute to the last message - in +ctx+ buffer. See +RTNL_ADD_MESSAGE()+. You need to provide attribute - +type+ which will be stored in +struct rtattr.rta_type+, optional payload - +data+ and payload size +len+ in bytes. If you don't want to add any payload, - set +data+ to +NULL+ and +len+ to 0. Returns 1 on success, 0 on error. +- +int NETLINK_ADD_ATTR(struct tst_netlink_context *ctx, unsigned short type, + const void *data, unsigned short len)+ – Adds new +struct nlattr+ attribute + to the last message in +ctx+ buffer. See +NETLINK_ADD_MESSAGE()+. You need + to provide attribute +type+ which will be stored in +struct nlattr.nla_type+, + optional payload +data+ and payload size +len+ in bytes. If you don't want + to add any payload, set +data+ to +NULL+ and +len+ to 0. Returns 1 on + success, 0 on error. -- +int RTNL_ADD_ATTR_STRING(struct tst_rtnl_context *ctx, unsigned short type, - const char *data)+ – Adds new string attribute to the last message in +ctx+ - buffer. Parameters and return value are the same as for +RTNL_ADD_ATTR()+, - except the payload length is calculated using +strlen()+. +- +int NETLINK_ADD_ATTR_STRING(struct tst_netlink_context *ctx, unsigned short + type, const char *data)+ – Adds new +struct nlattr+ string attribute to the + last message in +ctx+ buffer. Parameters and return value are the same as + for +NETLINK_ADD_ATTR()+, except the payload length is calculated using + +strlen()+. -- +int RTNL_ADD_ATTR_LIST(struct tst_rtnl_context *ctx, const struct - tst_rtnl_attr_list *list)+ – Adds a list of attributes to the last message - in +ctx+ buffer. See description of +struct tst_rtnl_attr_list+ and - +RTNL_ADD_MESSAGE()+ above. Returns the number of added attributes on - success (nested attributes are not counted), -1 on error. +- +int NETLINK_ADD_ATTR_LIST(struct tst_netlink_context *ctx, const struct + tst_netlink_attr_list *list)+ – Adds a list of +struct nlattr+ attributes + to the last message in +ctx+ buffer. See description of + +struct tst_netlink_attr_list+ and +NETLINK_ADD_MESSAGE()+ above. Returns + the number of added attributes on success (nested attributes are not + counted), -1 on error. + +- +int RTNL_ADD_ATTR(struct tst_netlink_context *ctx, unsigned short type, + const void *data, unsigned short len)+ – Adds new +struct rtattr+ attribute + to the last message in +ctx+ buffer. See +NETLINK_ADD_MESSAGE()+. You need + to provide attribute +type+ which will be stored in +struct rtattr.rta_type+, + optional payload +data+ and payload size +len+ in bytes. If you don't want + to add any payload, set +data+ to +NULL+ and +len+ to 0. Returns 1 on + success, 0 on error. + +- +int RTNL_ADD_ATTR_STRING(struct tst_netlink_context *ctx, unsigned short + type, const char *data)+ – Adds new +struct rtattr+ string attribute to the + last message in +ctx+ buffer. Parameters and return value are the same as + for +RTNL_ADD_ATTR()+, except the payload length is calculated using + +strlen()+. + +- +int RTNL_ADD_ATTR_LIST(struct tst_netlink_context *ctx, const struct + tst_netlink_attr_list *list)+ – Adds a list of +struct rtattr+ attributes + to the last message in +ctx+ buffer. See description of + +struct tst_netlink_attr_list+ and +NETLINK_ADD_MESSAGE()+ above. Returns + the number of added attributes on success (nested attributes are not + counted), -1 on error. Example Usage +++++++++++++ @@ -440,14 +467,14 @@ Example Usage #include #include "tst_test.h" -#include "tst_rtnetlink.h" +#include "tst_netlink.h" #include "tst_netdevice.h" ... void setup(void) { - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; int index, ret; in_addr_t addr; @@ -465,12 +492,12 @@ void setup(void) index = NETDEV_INDEX_BY_NAME("ltp_veth1"); info.ifa_index = index; - ctx = RTNL_CREATE_CONTEXT(); - RTNL_ADD_MESSAGE(ctx, &header, &info, sizeof(info)); + ctx = NETLINK_CREATE_CONTEXT(NETLINK_ROUTE); + NETLINK_ADD_MESSAGE(ctx, &header, &info, sizeof(info)); addr = inet_addr("192.168.123.45"); RTNL_ADD_ATTR(ctx, IFA_LOCAL, &addr, sizeof(addr)); - ret = RTNL_SEND_VALIDATE(ctx); - RTNL_DESTROY_CONTEXT(ctx); + ret = NETLINK_SEND_VALIDATE(ctx); + NETLINK_DESTROY_CONTEXT(ctx); if (!ret) { tst_brk(TBROK, "Failed to set ltp_veth1 address"); diff --git a/doc/Shell-Test-API.asciidoc b/doc/old/Shell-Test-API.asciidoc similarity index 99% rename from doc/Shell-Test-API.asciidoc rename to doc/old/Shell-Test-API.asciidoc index 4cf630da..c38fb069 100644 --- a/doc/Shell-Test-API.asciidoc +++ b/doc/old/Shell-Test-API.asciidoc @@ -188,7 +188,7 @@ space as default value is used. Of course, it's possible to use separate functio 1.2 Library environment variables and functions for shell ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Similarily to the C library various checks and preparations can be requested +Similarly to the C library various checks and preparations can be requested simply by setting right '$TST_FOO'. [options="header"] diff --git a/doc/namespaces-helper-tools.txt b/doc/old/namespaces-helper-tools.txt old mode 100755 new mode 100644 similarity index 100% rename from doc/namespaces-helper-tools.txt rename to doc/old/namespaces-helper-tools.txt diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 00000000..1b9a9845 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,7 @@ +# Use the same sphinx as on readthedocs.org. When updated, make sure +# sphinx-rtd-theme is compatible with sphinx. +sphinx==7.2.6 +sphinx-rtd-theme==2.0.0 + +linuxdoc==20231020 +sphinxcontrib-spelling==7.7.0 diff --git a/doc/rules.tsv b/doc/rules.tsv deleted file mode 100755 index 66dbdecc..00000000 --- a/doc/rules.tsv +++ /dev/null @@ -1,6 +0,0 @@ -ID DESCRIPTION -LTP-001 Library source files have tst_ prefix -LTP-002 TST_RET and TST_ERR are never modified by test library functions -LTP-003 Externally visible library symbols have the tst_ prefix -LTP-004 Test executable symbols are marked static -LTP-005 Array must terminate with a sentinel value (i.e. NULL or '{}') diff --git a/doc/spelling_wordlist b/doc/spelling_wordlist new file mode 100644 index 00000000..506e5bda --- /dev/null +++ b/doc/spelling_wordlist @@ -0,0 +1,161 @@ +aarch +akpm +amd +archiver +archs +args +asciidoc +autoconf +automake +backport +baz +btime +bufs +CCed +cgroup +cgroups +checksum +checksums +cmds +codebase +compat +config +cpus +ctrls +datafiles +dependants +dev +devfs +dio +distro +distros +docparse +doio +executables +fd +filesystem +filesystems +fixup +fs +fstrict +fuzzsync +gcc +gdb +github +gitignore +glibc +growfiles +hugepage +iogen +kbuild +kconfigs +kver +ld +le +libc +libltp +libs +linkable +linux +lockdown +ltp +lwn +Maipo +Makefile +Makefiles +mem +mnt +mntpoint +msg +namespace +namespaces +onwards +openSUSE +patchset +pkgconf +posix +ppc +pre +pre +preprocessor +rebase +reflog +reinit +repo +rofs +runtest +scall +secureboot +sourceware +statx +stdout +structs +stx +subdirectory +subshell +subshells +syscall +syscalls +tcnt +tconf +teardown +testcase +testcases +testsuite +testsuites +tmp +tmpdir +toolchain +toolchains +torvalds +tst +uClibc +ulimit +un +userland +userspace +vCPU +vCPUs +vendoring +vger +wallclock +pid +TBROK +mkfs +hugepages +hugetlbfs +printf +scanf +argv +argc +pthread +futex +hdr +brk +iovec +iov +strdup +aprintf +alloc +hexd +tcases +strsig +strstatus +strerrno +libcap +capset +capget +musl +setrlimit +rlim +optstr +tmpfs +nodev +sys +proc +arg +ver +bitwise +dereferenced +allocator +ptr diff --git a/doc/users/quick_start.rst b/doc/users/quick_start.rst new file mode 100644 index 00000000..e73175e3 --- /dev/null +++ b/doc/users/quick_start.rst @@ -0,0 +1,127 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Installation and tests execution +================================ + +Basics requirements to build LTP are the following: + +* git +* autoconf +* automake +* make +* gcc +* m4 +* pkgconf / pkg-config +* libc headers +* linux headers + +.. code-block:: console + + $ git clone --recurse-submodules https://github.com/linux-test-project/ltp.git + $ cd ltp + $ make autotools + $ ./configure + +.. note:: + + For optional library dependencies, take a look at the scripts inside :master:`ci/` + directory. + +Running single tests +-------------------- + +LTP provides the possibility to build and run single tests: + +.. code-block:: console + + $ cd testcases/kernel/syscalls/foo + $ make + $ PATH=$PATH:$PWD ./foo01 + +Shell testcases are a bit more complicated, since they need to setup ``PATH`` +as well as to compiled binary helpers: + +.. code-block:: console + + $ cd testcases/lib + $ make + $ cd ../commands/foo + $ PATH=$PATH:$PWD:$PWD/../../lib/ ./foo01.sh + +Open Posix Testsuite has it's own build system which needs Makefiles to be +generated first: + +.. code-block:: console + + $ ./configure --with-open-posix-testsuite + $ cd testcases/open_posix_testsuite/ + $ make generate-makefiles + $ cd conformance/interfaces/foo + $ make + $ ./foo_1-1.run-test + +Compiling and installing all testcases +-------------------------------------- + +To compile all tests is really simple: + +.. code-block:: console + + $ make + + $ # install LTP inside /opt/ltp by default + $ make install + +.. note:: + + Some tests will be disabled if ``configure`` script won't find the build + dependencies. + +Running tests +------------- + +To run all the test suites + +.. code-block:: console + + $ cd /opt/ltp + + $ # run syscalls testing suite + $ ./kirk -U ltp -f syscalls + +.. note:: + + Many test cases have to be executed as root. + +Test suites (e.g. syscalls) are defined in the ``runtest`` directory. Each file +contains a list of test cases in a simple format. + +Each test case has its own executable or script that can directly executed: + +.. code-block:: console + + $ testcases/bin/abort01 + + $ # some tests have arguments + $ testcases/bin/mesgq_nstest -m none + + $ # vast majority of tests have a help + $ testcases/bin/ioctl01 -h + + $ # Many require certain environment variables to be set + $ LTPROOT=/opt/ltp PATH="$PATH:$LTPROOT/testcases/bin" testcases/bin/wc01.sh + +Most commonly, the ``PATH`` variable needs to be set and also ``LTPROOT``, but +there are a number of other variables which usually ``kirk`` sets for you. + +.. note:: + + All shell scripts need the ``PATH`` to be set. However, this is not limited + to shell scripts and some C based tests need environment variables as well. + They usually raise a configuration error when this is needed. + +Network tests +------------- + +Network tests usually require a certain setup that is described in +:master:`testcases/network/README.md`. diff --git a/doc/users/setup_tests.rst b/doc/users/setup_tests.rst new file mode 100644 index 00000000..38976f3b --- /dev/null +++ b/doc/users/setup_tests.rst @@ -0,0 +1,120 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Tests setup +=========== + +The internal LTP library provides a set of features that permits to customize +tests behavior by setting environment variables and using specific tests +arguments. + +Library environment variables +----------------------------- + +Following environment variables are expected to be set by LTP users. Therefore, +with some exceptions, they have ``LTP_`` prefix. Environment variables with +``TST_`` prefix are used inside LTP shell API and should **not** be set by +users. + +.. list-table:: + :header-rows: 1 + + * - Variable + - Note + + * - KCONFIG_PATH + - The path to the kernel config file, (if not set, it tries the usual paths + ``/boot/config-RELEASE`` or ``/proc/config.gz``) + + * - KCONFIG_SKIP_CHECK + - Skip kernel config check if variable set (not set by default) + + * - LTPROOT + - Prefix for installed LTP. **Should be always set**, since some tests + need it to use data files (``LTP_DATAROOT``). LTP is by default installed + into ``/opt/ltp`` + + * - LTP_COLORIZE_OUTPUT + - By default LTP colorizes it's output unless it's redirected to a pipe or + file. Force colorized output behavior: ``y`` or ``1``: always colorize, + ``n`` or ``0``: never colorize. + + * - LTP_DEV + - Path to the block device to be used. C Language: ``.needs_device = 1``. + Shell language: ``TST_NEEDS_DEVICE=1``. + + * - LTP_REPRODUCIBLE_OUTPUT + - When set to ``1`` or ``y`` discards the actual content of the messages + printed by the test (suitable for a reproducible output). + + * - LTP_SINGLE_FS_TYPE + - Specifies single filesystem to run the test on instead all supported + (for tests with ``.all_filesystems``). + + * - LTP_FORCE_SINGLE_FS_TYPE + - Testing only. Behaves like LTP_SINGLE_FS_TYPE but ignores test skiplists. + + * - LTP_DEV_FS_TYPE + - Filesystem used for testing (default: ``ext2``). + + * - LTP_TIMEOUT_MUL + - Multiplies timeout, must be number >= 0.1 (> 1 is useful for slow + machines to avoid unexpected timeout). It's mainly for shell API, which + does not have LTP_RUNTIME_MUL. In C API it scales the default 30 sec + safety margin, probably LTP_RUNTIME_MUL should be used instead. + + * - LTP_RUNTIME_MUL + - Multiplies maximal test iteration runtime. Tests that run for more than a + second or two are capped on runtime. You can scale the default runtime + both up and down with this multiplier. This is not yet implemented in the + shell API. + + * - LTP_IMA_LOAD_POLICY + - Load IMA example policy, see :master:`testcases/kernel/security/integrity/ima/README.md`. + + * - LTP_VIRT_OVERRIDE + - Overrides virtual machine detection in the test library. Setting it to + empty string, tells the library that system is not a virtual machine. + Other possible values are ``kvm``, ``xen``, ``zvm`` and ``microsoft`` + that describe different types supervisors. + + * - PATH + - It's required to adjust path: ``PATH="$PATH:$LTPROOT/testcases/bin"`` + + * - TMPDIR + - Base directory for template directory (C language: ``.needs_tmpdir = 1`` + and shell: ``TST_NEEDS_TMPDIR=1``). Must be an absolute path (default: + '/tmp'). + + * - LTP_NO_CLEANUP + - Disable running test cleanup (defined in ``TST_CLEANUP``). + Shell API only. + + * - LTP_ENABLE_DEBUG + - Enable debug info (value ``1`` or ``y``). Equivalent of ``-D`` parameter. + +Environment variables for network tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +See :master:`testcases/network/README.md`. + +Test execution time and timeout +------------------------------- + +The limit on how long a test can run does compose of two parts: ``runtime`` +and ``timeout``. The limit does apply to a single test variant. That means, for +example, that tests which run for all available filesystems will apply this +limit for a single filesystem only. + +The ``runtime`` is a cap on how long the ``run()`` function can take and for +most testcases this part is set to zero. For tests that do run for more than a +second or two the ``runtime`` has to be defined and the ``run()`` function +has to check actively how much runtime is left. + +Test runtime can be scaled up and down with ``LTP_RUNTIME_MUL`` environment +variable or set on a command-line by the ``-I`` parameter. However, +setting the runtime too low will cause long running tests to exit prematurely, +possibly before having a chance to actually test anything. + +The timeout is a limit for test setup and cleanup and it's also a safety +margin for the runtime accounting. It's currently set to 30 seconds but it may +change later. If your target machine is too slow, it can be scaled up with the +``LTP_TIMEOUT_MUL`` environment variable. diff --git a/doc/users/stats.rst b/doc/users/stats.rst new file mode 100644 index 00000000..7073442a --- /dev/null +++ b/doc/users/stats.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Statistics +========== + +In this section we collect some statistics related to the current state of +LTP tests. + +.. include:: ../_static/syscalls.rst diff --git a/doc/users/supported_systems.rst b/doc/users/supported_systems.rst new file mode 100644 index 00000000..dabb5883 --- /dev/null +++ b/doc/users/supported_systems.rst @@ -0,0 +1,114 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Supported systems +================= + +LTP `master `_ +branch is build tested in +`GitHub Actions `_. + +.. note:: + + There is no CI for the actual test runs. + +Kernel version +-------------- + +Minimal supported kernel version is **4.4**. + +Oldest build tested distributions +--------------------------------- + +.. list-table:: + :header-rows: 1 + + * - Distro + - Kernel + - glibc + - gcc + - clang + + * - openSUSE Leap 42.2 + - 4.4 + - 2.22 + - 4.8.5 + - \- + + * - Ubuntu 18.04 LTS bionic + - 4.15 + - 2.27 + - 7.3.0 + - \- + + * - Debian 11 (bullseye) + - 5.10 + - 2.31 + - 10.2.1 + - 11.0.1 + +For a full list of build tested distros, please check :master:`.github/workflows/ci-docker-build.yml`. + +Older distributions are not officially supported, which means that it +may or may not work. It all depends on your luck. It should be possible +to compile latest LTP even on slightly older distributions than we +support with a few manual tweaks, e.g. disabling manually tests for +newly added syscalls, etc. **Trivial fixes/workarounds may be accepted, +but users are encouraged to move to a newer distro.** + +If latest LTP cannot be compiled even with some amount of workarounds, +you may result to older LTP releases, however these are **not** supported +in any way. Also if you are trying to run LTP on more than 10 years old +distribution you may as well reconsider you life choices. + +Build tested architectures +-------------------------- + +.. list-table:: + :header-rows: 1 + + * - Architecture + - Build + + * - x86_64 + - native + + * - x86 emulation + - native + + * - aarch64 + - cross compilation + + * - ppc64le + - cross compilation + + * - s390x + - cross compilation + +Supported C libraries +--------------------- + +.. list-table:: + :header-rows: 1 + + * - C library + - Note + + * - `glibc `_ + - Targeted libc, tested both compilation and actual test results. + + * - `uClibc-ng `_ + - Although not being tested, it should work as it attempt to maintain a glibc compatible interface. + + * - `uClibc `_ + - Older uClibc might have problems. + + * - `musl `_ + - Not yet fully supported. Check :master:`ci/alpine.sh` script. + + * - Android + - Please use `AOSP fork `_. + +C version +--------- + +LTP is compiled with ``-std=gnu99``. diff --git a/doc/users/test_catalog.rst b/doc/users/test_catalog.rst new file mode 100644 index 00000000..b1674f9d --- /dev/null +++ b/doc/users/test_catalog.rst @@ -0,0 +1,7 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Test catalog +============ + +.. include:: ../_static/tests.rst + diff --git a/doc/users/testers_guide.rst b/doc/users/testers_guide.rst new file mode 100644 index 00000000..9c1ddb98 --- /dev/null +++ b/doc/users/testers_guide.rst @@ -0,0 +1,155 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Testers guide to the Linux test project +======================================= + +While we try to make LTP work out of the box as much as possible there are +still many things that testers need to consider before the actual testing +starts. It's advisable to make a test plan in order to asses and formalize the +expected test coverage or even just sit down for a while and consider different +aspects of the problem at hand. + + +Is testing even required? +------------------------- + +Some may argue that testing the Linux kernel locally is unnecessary because it +is already thoroughly tested upstream and considered stable. While it's true +that upstream releases generally go through extensive validation, including +test suites like LTP, stability is only guaranteed when you use the upstream +kernel sources and configuration exactly as released. + +This assumption breaks down once you apply any changes: whether that's +modifying the source code, enabling/disabling different `.config` options, or +backporting patches. Such changes can introduce subtle bugs or unintended +behavior, even if the upstream kernel is stable. + +For example, backporting patches without their full dependency chain can lead +to unexpected regressions. Therefore, it’s crucial to test your own kernel +builds in the environment where they will actually run, using tools like LTP to +catch issues that does not exists or are not triggered in the upstream +configuration. + + +Multi dimensionality +-------------------- + +First of all kernel testing is a multi dimensional problem, just compiling and +running LTP will give you some coverage but very likely not enough. There are +several big gaps that may be easily missed. + +For example 64bit Linux kernel provides compatibility layer for 32bit +applications whose code quality is usually a bit worse than the 64bit ABI. +Hence recompiling LTP with `-m32` in compiler flags and running both 64bit and +32bit test binaries is a good start. If you try to make an argument that your +application does not need 32bit support it's better to disable the compat layer +completely since it's possible source of security bugs. + +Another dimension is the number of architectures you need to test, for a +general distribution testing you may end up with a couple of them. Different +architectures have different platform code as well as differences in memory +orderings, etc. that all means that running tests on one architecture out of +several will give you incomplete coverage. + +Since most of the POSIX API deals with files, the choice of filesystem for the +testing changes the focus and coverage too. LTP defaults to using `/tmp/` as a +temporary directory for the tests. If `/tmp/` is mounted as tmpfs subset of +tests will be skipped, if that is the case it's advisable to point environment +variable `TMPDIR` to a path with a different filesystem instead. Then there are +tests that format a device with a filesystem. LTP defaults to `ext2` and loop +devices for these testcases, that can be changed with environment variables as +well. Lastly but not least a few testcases repeat the test for all supported +filesystem, if you are interested in testing on a single filesystem only, you +can limit these tests to a single filesystem too. See the tests setup for a +comprehensive list of the `evironment variables +`_. + +Then you also have to decide if you are going to run tests in virtual machine +e.g. `qemu-kvm`, on bare metal or both. Testing in virtual machine will give you +about 90% of the coverage for bare metal and vice versa. + +There are other options worth of consideration too, Linux kernel has many +debugging options that are usually disabled on runtime since they incur +significant performance penalty. Having a few more LTP test runs with different +debug options enabled e.g. `KASAN +`_ or `KMEMLEAK +`_ may help +catch bugs before they materialize in production. + +In practice your test matrix may easily explode and you may end up with dozens +of differently configured testruns based on different considerations. The hard +task at hand is not to have too many since computing power is not an infinite +resource and does not scale that easily. If you managed to read up to this +point *"Don't Panic!"* things are almost never as bad as they may seem at first +glance. + +It's a good idea to start small with an environment that models your +production. Once that works well you can try different configurations. Select +a few interesting ones and run them for some time in order to get an idea of +their usefulness. If you are feeling adventurous you may try to measure and +compare actual test coverage with one of the tools such as `gcov +`_ and `lcov +`_. If you do so do not fall into a +trap of attempting to have 100% line coverage. Having 100% of lines executed +during the test does not mean that your test coverage is 100%. Good tests +validate much more than just how much code from the tested binary was executed. + +You may need to sacrifice some coverage in order to match the tests runtime to +the available computing power. When doing so `Pareto principle +`_ is your friend. + + +Test scope +---------- + +So far we were talking about a code coverage from a point of maximizing test +coverage while keeping our test matrix as small as possible. While that is a +noble goal it's not the universal holy grail of testing. Different use cases +have different considerations and scope. For a testing before a final release +such testing is very desirable, however for a continuous integration or smoke +testing the main requirement is that feedback loops are as short as possible. + +When a developer changes the kernel and submits the changes to be merged it's +desirable to run some tests. Again the hard question is which tests. If we run +all possible tests in all possible combinations it may take a day or two and +the developer will move to a different tasks before the tests have a chance to +finish. If you multiply that by a number of developers in the team you may end +up in a situation where a developer will retire before tests for his patch may +have had a chance to finish. + +In this case careful selection of tests is even more important. Having less is +more in this context. One of the first ideas for CI is to skip tests that run +for more than a second or so, happily this can be easily done with `kirk +`_. In the future we may want to +explore some heuristics that would map the code changes in kernel into a subset +of tests, which would allow for a very quick feedback. + + +Debugging test failures +----------------------- + +You may think that you will enjoy some rest once you have your test matrix +ready and your tests are running. Unfortunately that's where the actual work +starts. Debugging test failures is probably the hardest part of the testing +process. In some cases failures are easily reproducible and it's not that hard +to locate the bug, either in the test or in the kernel itself. There are +however, quite common, cases where the test failure reproduces only in 10% or +even 1% of the test runs. Sometimes tests are not failing in isolation, that is +because operating system has a huge internal state and a test failure manifests +only after running right sequence of tests. All of that does not mean that +there is no bug, that usually means that the bug depends on more prerequisites +that have to manifest at the right time in order to trigger the failure. Sadly +for modern systems that are asynchronous in nature such bugs are more and more +common. + +The debugging process itself is not complicated by its nature. You have to +attempt to understand the failure by checking the logs, reading and +understanding the source code, debugging with strace, gdb, etc. Then form a +hypothesis and either prove or disprove it. Rinse and repeat until you end up +with a clear description of what went wrong. Hopefully you will manage to find +the root cause, but you should not be discouraged, if you do not. Debugging +kernel bugs takes a lot of experience and skill one can say as much as is +needed to write the kernel code. + + +Happy testing! diff --git a/docparse/.gitignore b/docparse/.gitignore deleted file mode 100755 index d786a476..00000000 --- a/docparse/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/*.txt -/docbook-xsl.css -/metadata.html -/metadata.pdf -/metadata.chunked/ diff --git a/docparse/Makefile b/docparse/Makefile deleted file mode 100755 index 20851fba..00000000 --- a/docparse/Makefile +++ /dev/null @@ -1,69 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2019 Cyril Hrubis -# Copyright (c) 2020 Petr Vorel - -top_srcdir ?= .. - -include $(top_srcdir)/include/mk/env_pre.mk -include $(top_srcdir)/include/mk/functions.mk - -ifeq ($(METADATA_GENERATOR),asciidoctor) -METADATA_GENERATOR_CMD := asciidoctor -METADATA_GENERATOR_PARAMS := -d book metadata.txt -METADATA_GENERATOR_PARAMS_HTML := -b xhtml -METADATA_GENERATOR_PARAMS_PDF := -b pdf -r asciidoctor-pdf -else ifeq ($(METADATA_GENERATOR),asciidoc) -METADATA_GENERATOR_CMD := a2x -METADATA_GENERATOR_PARAMS := --xsltproc-opts "--stringparam toc.section.depth 1" -d book -L --resource="$(PWD)" metadata.txt -METADATA_GENERATOR_PARAMS_HTML := -f xhtml -METADATA_GENERATOR_PARAMS_PDF := -f pdf -METADATA_GENERATOR_PARAMS_HTML_CHUNKED := -f chunked -else ifeq ($(METADATA_GENERATOR),) -$(error 'METADATA_GENERATOR' not configured, run ./configure in the root directory) -else -$(error '$(METADATA_GENERATOR)' not supported, only asciidoctor and asciidoc are supported) -endif - -ifdef VERBOSE -METADATA_GENERATOR_PARAMS += -v -endif - -CLEAN_TARGETS := *.css *.js *.txt - -ifeq ($(WITH_METADATA_HTML),yes) -MAKE_TARGETS += metadata.html -ifneq ($(METADATA_GENERATOR_PARAMS_HTML_CHUNKED),) -MAKE_TARGETS += metadata.chunked -endif -endif - -ifeq ($(WITH_METADATA_PDF),yes) -MAKE_TARGETS += metadata.pdf -endif - -INSTALL_DIR = metadata -INSTALL_TARGETS = *.css *.js - -ifndef METADATA_GENERATOR -METADATA_GENERATOR := asciidoctor -endif - -txt: ${abs_top_builddir}/metadata/ltp.json - $(abs_srcdir)/testinfo.pl $< - -ifeq ($(WITH_METADATA_HTML),yes) -metadata.html: txt - $(METADATA_GENERATOR_CMD) $(METADATA_GENERATOR_PARAMS) $(METADATA_GENERATOR_PARAMS_HTML) - -ifneq ($(METADATA_GENERATOR_PARAMS_HTML_CHUNKED),) -metadata.chunked: txt - $(METADATA_GENERATOR_CMD) $(METADATA_GENERATOR_PARAMS) $(METADATA_GENERATOR_PARAMS_HTML_CHUNKED) -endif -endif - -ifeq ($(WITH_METADATA_PDF),yes) -metadata.pdf: txt - $(METADATA_GENERATOR_CMD) $(METADATA_GENERATOR_PARAMS) $(METADATA_GENERATOR_PARAMS_PDF) -endif - -include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/docparse/testinfo.pl b/docparse/testinfo.pl deleted file mode 100755 index 78433c40..00000000 --- a/docparse/testinfo.pl +++ /dev/null @@ -1,522 +0,0 @@ -#!/usr/bin/perl -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2019 Cyril Hrubis -# Copyright (c) 2020-2021 Petr Vorel - -use strict; -use warnings; - -use JSON qw(decode_json); -use Cwd qw(abs_path); -use File::Basename qw(dirname); - -use constant OUTDIR => dirname(abs_path($0)); - -# tags which expect git tree, also need constant for URL -our @TAGS_GIT = ("linux-git", "linux-stable-git", "glibc-git", "musl-git"); - -# tags should map these in lib/tst_test.c -use constant LINUX_GIT_URL => "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id="; -use constant LINUX_STABLE_GIT_URL => "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id="; -use constant GLIBC_GIT_URL => "https://sourceware.org/git/?p=glibc.git;a=commit;h="; -use constant MUSL_GIT_URL => "https://git.musl-libc.org/cgit/musl/commit/src/linux/clone.c?id="; -use constant CVE_DB_URL => "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-"; - -sub load_json -{ - my ($fname, $mode) = @_; - local $/; - - open(my $fh, '<', $fname) or die("Can't open $fname $!"); - - return <$fh>; -} - -sub log_info -{ - my $msg = shift; - print STDERR "INFO: $msg\n"; -} - -sub log_warn -{ - my $msg = shift; - print STDERR "WARN: $msg\n"; -} - -sub print_asciidoc_page -{ - my ($fh, $json, $title, $content) = @_; - - print $fh <{'testsuite'}->{'url'}); - $content .= print_defined("Version", $json->{'testsuite'}->{'version'}); - $content .= print_defined("Default timeout", $json->{'defaults'}->{'timeout'}, "seconds"); - - return $content; -} - -sub uniq { - my %seen; - grep !$seen{$_}++, @_; -} - -sub get_test_names -{ - my @names = @{$_[0]}; - my ($letter, $prev_letter); - my $content; - - for my $name (sort @names) { - $letter = substr($name, 0, 1); - if (defined($prev_letter) && $letter ne $prev_letter) { - $content .= "\n"; - } - - $content .= reference($name, delimiter => " "); - $prev_letter = $letter; - } - $content .= "\n"; - - return $content; -} - -sub get_test_letters -{ - my @names = @{$_[0]}; - my $letter; - my $prev_letter = ""; - my $content; - - for (@names) { - $_ = substr($_, 0, 1); - } - @names = uniq(@names); - - for my $letter (@names) { - $content .= reference($letter); - } - $content .= "\n"; - - return $content; -} - -sub tag2title -{ - my $tag = shift; - return code(".$tag"); -} - -sub get_filters -{ - my $json = shift; - my %data; - - while (my ($k, $v) = each %{$json->{'tests'}}) { - for my $j (keys %{$v}) { - next if ($j eq 'fname' || $j eq 'doc'); - $data{$j} = () unless (defined($data{$j})); - - if ($j eq 'tags') { - for my $tags (@{$v}{'tags'}) { - for my $tag (@$tags) { - my $k2 = $$tag[0]; - my $v2 = $$tag[1]; - $data{$j}{$k2} = () unless (defined($data{$j}{$k2})); - push @{$data{$j}{$k2}}, $k unless grep{$_ eq $k} @{$data{$j}{$k2}}; - } - } - } else { - push @{$data{$j}}, $k unless grep{$_ eq $k} @{$data{$j}}; - } - } - } - return \%data; -} - -sub content_filter -{ - my $k = $_[0]; - my $title = $_[1]; - my $desc = $_[2]; - my $h = $_[3]; - my ($letter, $prev_letter, $content); - - $content = label($k); - $content .= $title; - $content .= paragraph("Tests containing $desc flag."); - - $content .= get_test_names(\@{$h}); - - return $content; -} - -sub content_filters -{ - my $json = shift; - my $data = get_filters($json); - my %h = %$data; - my $content; - - for my $k (sort keys %$data) { - my $title = tag2title($k); - if (ref($h{$k}) eq 'HASH') { - $content .= label($k); - $content .= h2($title); - for my $k2 (sort keys %{$h{$k}}) { - my $title2 = code($k2); - $content .= content_filter($k2, h3($title2), "$title $title2", $h{$k}{$k2}); - } - } else { - $content .= content_filter($k, h2($title), $title, \@{$h{$k}}); - } - } - - return $content; -} - -sub tag2env -{ - my $tag = shift; - $tag =~ s/-/_/g; - return uc($tag); -} - -sub detect_git -{ - my %data; - - for my $tag (@TAGS_GIT) { - my $env = tag2env($tag); - - unless (defined $ENV{$env} && $ENV{$env}) { - log_warn("git repository $tag not defined. Define it in \$$env"); - next; - } - - unless (-d $ENV{$env}) { - log_warn("\$$env does not exit ('$ENV{$env}')"); - next; - } - - if (system("which git >/dev/null")) { - log_warn("git not in \$PATH ('$ENV{'PATH'}')"); - next; - } - - chdir($ENV{$env}); - if (!system("git log -1 > /dev/null")) { - log_info("using '$ENV{$env}' as $env repository"); - $data{$tag} = $ENV{$env}; - } else { - log_warn("git failed, git not installed or \$$env is not a git repository? ('$ENV{$env}')"); - } - chdir(OUTDIR); - } - - return \%data; -} - -sub content_all_tests -{ - my $json = shift; - my @names = sort keys %{$json->{'tests'}}; - my $letters = paragraph(get_test_letters(\@names)); - my $git_url = detect_git(); - my $tmp = undef; - my $printed = ""; - my $content; - - $content .= paragraph("Total $#names tests."); - $content .= $letters; - $content .= get_test_names(\@names); - - for my $name (@names) { - my $letter = substr($name, 0, 1); - - if ($printed ne $letter) { - $content .= label($letter); - $content .= h2($letter); - $printed = $letter; - } - - $content .= hr() if (defined($tmp)); - $content .= label($name); - $content .= h3($name); - $content .= $letters; - - if (defined($json->{'testsuite'}->{'scm_url_base'}) && - defined($json->{'tests'}{$name}{fname})) { - $content .= paragraph(html_a(tag_url("fname", $json->{'tests'}{$name}{fname}, - $json->{'testsuite'}->{'scm_url_base'}), "source")); - } - - if (defined $json->{'tests'}{$name}{doc}) { - for my $doc (@{$json->{'tests'}{$name}{doc}}) { - - # fix formatting for asciidoc [DOCUMENTATION] => *Documentation* - if ($doc =~ s/^\[(.*)\]$/$1/) { - $doc = paragraph(bold(ucfirst(lc($doc)))); - } - - $content .= "$doc\n"; - } - $content .= "\n"; - } - - if ($json->{'tests'}{$name}{timeout}) { - if ($json->{'tests'}{$name}{timeout} eq -1) { - $content .= paragraph("Test timeout is disabled"); - } else { - $content .= paragraph("Test timeout is $json->{'tests'}{$name}{timeout} seconds"); - } - } else { - $content .= paragraph("Test timeout defaults to $json->{'defaults'}->{'timeout'} seconds"); - } - - my $tmp2 = undef; - for my $k (sort keys %{$json->{'tests'}{$name}}) { - my $v = $json->{'tests'}{$name}{$k}; - next if ($k eq "tags" || $k eq "fname" || $k eq "doc"); - if (!defined($tmp2)) { - $content .= table . "|Key|Value\n\n" - } - - $content .= "|" . reference($k, text => tag2title($k)) . "\n|"; - - if (ref($v) eq 'ARRAY') { - # two dimensional array - if (ref(@$v[0]) eq 'ARRAY') { - for my $v2 (@$v) { - $content .= paragraph(table_escape(join(' ', @$v2))); - } - } else { - # one dimensional array - $content .= table_escape(join(', ', @$v)); - } - } else { - # plain content - $content .= table_escape($v); - } - - $content .= "\n"; - - $tmp2 = 1; - } - if (defined($tmp2)) { - $content .= table . "\n"; - } - - $tmp2 = undef; - my %commits; - my @sorted_tags = sort { $a->[0] cmp $b->[0] } @{$json->{'tests'}{$name}{tags} // []}; - - for my $tag (@sorted_tags) { - if (!defined($tmp2)) { - $content .= table . "|Tag|Info\n" - } - my $k = @$tag[0]; - my $v = @$tag[1]; - my $url; - - if (defined($$git_url{$k})) { - $commits{$k} = () unless (defined($commits{$k})); - unless (defined($commits{$k}{$v})) { - chdir($$git_url{$k}); - $commits{$k}{$v} = `git log --pretty=format:'%s' -1 $v`; - chdir(OUTDIR); - } - $v .= ' ("' . $commits{$k}{$v} . '")'; - } - - $url = tag_url($k, @$tag[1]); - if ($url) { - $v = html_a($url, $v); - } - - # tag value value can be split into more lines if too long - # i.e. URL in known-fail - for (@$tag[2 .. $#$tag]) { - $v .= " $_"; - } - - $content .= "\n|" . reference($k) . "\n|$v\n"; - $tmp2 = 1; - } - if (defined($tmp2)) { - $content .= table . "\n"; - } - - $tmp = 1; - } - - return $content; -} - - -my $json = decode_json(load_json($ARGV[0])); - -my $config = [ - { - file => "about.txt", - title => h2("About $json->{'testsuite'}->{'name'}"), - content => \&content_about, - }, - { - file => "filters.txt", - title => h1("Test filtered by used flags"), - content => \&content_filters, - }, - { - file => "all-tests.txt", - title => h1("All tests"), - content => \&content_all_tests, - }, -]; - -sub print_asciidoc_main -{ - my $config = shift; - my $file = "metadata.txt"; - my $content; - - open(my $fh, '>', $file) or die("Can't open $file $!"); - - $content = <{'testsuite'}->{'short_name'} . " test catalog"), $content); -} - -for my $c (@{$config}) { - open(my $fh, '>', $c->{'file'}) or die("Can't open $c->{'file'} $!"); - print_asciidoc_page($fh, $json, $c->{'title'}, $c->{'content'}->($json)); -} - -print_asciidoc_main($config); diff --git a/include/Makefile b/include/Makefile index 25e96df9..6b31b046 100755 --- a/include/Makefile +++ b/include/Makefile @@ -18,8 +18,8 @@ MAKE_TARGETS := distclean:: clean ac-distclean maintainer-clean:: distclean ac-maintainer-clean ac-clean ac-distclean:: - $(RM) -f config.h -ac-maintainer-clean:: + $(RM) -f config.h lapi/syscalls.h stamp-h1 +ac-maintainer-clean:: ac-clean $(RM) -f config.h.in vpath %.h $(abs_srcdir) diff --git a/include/lapi/arch_prctl.h b/include/lapi/arch_prctl.h new file mode 100644 index 00000000..a4e7f845 --- /dev/null +++ b/include/lapi/arch_prctl.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + */ +#ifndef LAPI_ARCH_PRCTL_H__ +#define LAPI_ARCH_PRCTL_H__ + +#include "config.h" + +#ifdef HAVE_ASM_PRCTL_H +# include +#endif + +#ifndef ARCH_GET_CPUID +# define ARCH_GET_CPUID 0x1011 +#endif + +#ifndef ARCH_SET_CPUID +# define ARCH_SET_CPUID 0x1012 +#endif + +#endif /* LAPI_ARCH_PRCTL_H__ */ diff --git a/include/lapi/blkdev.h b/include/lapi/blkdev.h new file mode 100644 index 00000000..3ee058ce --- /dev/null +++ b/include/lapi/blkdev.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Linux Test Project + * Li Wang + */ + +#ifndef LAPI_BLKDEV_H__ +#define LAPI_BLKDEV_H__ + +#ifdef HAVE_LINUX_BLKDEV_H +#include +#endif + +/* Define BLK_MAX_BLOCK_SIZE for older kernels */ +#ifndef BLK_MAX_BLOCK_SIZE +#define BLK_MAX_BLOCK_SIZE 0x00010000 /* 64K */ +#endif + +#endif /* LAPI_BLKDEV_H */ diff --git a/include/lapi/capability.h b/include/lapi/capability.h index 17ec107b..d3cc121f 100755 --- a/include/lapi/capability.h +++ b/include/lapi/capability.h @@ -10,14 +10,10 @@ #ifdef HAVE_SYS_CAPABILITY_H # include -/** - * Some old libcap-devel(1.96~2.16) define _LINUX_TYPES_H in - * sys/capability.h that makes ltp-lib cann't include linux/types.h - * essentially. Here undefine it if include such old header-file. - */ -# ifndef HAVE_NEWER_LIBCAP -# undef _LINUX_TYPES_H -# endif +#endif + +#ifndef CAP_NET_BIND_SERVICE +# define CAP_NET_BIND_SERVICE 10 #endif #ifndef CAP_NET_RAW @@ -36,18 +32,26 @@ # define CAP_SYS_ADMIN 21 #endif +#ifndef CAP_SYS_NICE +# define CAP_SYS_NICE 23 +#endif + #ifndef CAP_SYS_TIME # define CAP_SYS_TIME 25 #endif -#ifndef CAP_AUDIT_READ -# define CAP_AUDIT_READ 37 -#endif - #ifndef CAP_SYS_RESOURCE # define CAP_SYS_RESOURCE 24 #endif +#ifndef CAP_MKNOD +# define CAP_MKNOD 27 +#endif + +#ifndef CAP_AUDIT_READ +# define CAP_AUDIT_READ 37 +#endif + #ifndef CAP_BPF # define CAP_BPF 39 #endif diff --git a/include/lapi/common_timers.h b/include/lapi/common_timers.h index 884c997a..6a615c3f 100755 --- a/include/lapi/common_timers.h +++ b/include/lapi/common_timers.h @@ -7,6 +7,7 @@ #ifndef LAPI_COMMON_TIMERS_H__ #define LAPI_COMMON_TIMERS_H__ +#include #include "config.h" #include "lapi/syscalls.h" #include "lapi/posix_clocks.h" @@ -30,6 +31,8 @@ static const clock_t clock_list[] = { /* MAX_CLOCKS is the maximum number of clock sources supported by kernel */ #define MAX_CLOCKS 16 +#define MAX_AUX_CLOCKS 8 + #define CLOCK_TO_STR(def_name) \ case def_name: \ return #def_name; diff --git a/include/lapi/fanotify.h b/include/lapi/fanotify.h index 4bd1a113..8d04c8f2 100644 --- a/include/lapi/fanotify.h +++ b/include/lapi/fanotify.h @@ -32,6 +32,10 @@ #define FAN_REPORT_DFID_NAME_TARGET (FAN_REPORT_DFID_NAME | \ FAN_REPORT_FID | FAN_REPORT_TARGET_FID) #endif +#ifndef FAN_REPORT_FD_ERROR +#define FAN_REPORT_FD_ERROR 0x00002000 +#endif + /* Non-uapi convenience macros */ #ifndef FAN_REPORT_DFID_NAME_FID @@ -105,6 +109,9 @@ #ifndef FAN_FS_ERROR #define FAN_FS_ERROR 0x00008000 #endif +#ifndef FAN_PRE_ACCESS +#define FAN_PRE_ACCESS 0x00100000 +#endif #ifndef FAN_RENAME #define FAN_RENAME 0x10000000 #endif @@ -117,6 +124,16 @@ #define FAN_EPIDFD -2 #endif +/* errno other than EPERM can specified in upper byte of deny response */ +#ifndef FAN_DENY_ERRNO +#define FAN_ERRNO(err) (((((__u32)(err)) & 0xff) << 24)) +#define FAN_DENY_ERRNO(err) (FAN_DENY | FAN_ERRNO(err)) +#endif + +#ifndef FAN_RESPONSE_ERRNO +#define FAN_RESPONSE_ERRNO(res) ((int)((res) >> 24)) +#endif + /* Flags required for unprivileged user group */ #define FANOTIFY_REQUIRED_USER_INIT_FLAGS (FAN_REPORT_FID) @@ -130,6 +147,8 @@ #define LTP_ALL_PERM_EVENTS (FAN_OPEN_PERM | FAN_OPEN_EXEC_PERM | \ FAN_ACCESS_PERM) +#define LTP_PRE_CONTENT_EVENTS (FAN_PRE_ACCESS) + struct fanotify_group_type { unsigned int flag; const char *name; @@ -162,6 +181,9 @@ typedef struct { #ifndef FAN_EVENT_INFO_TYPE_ERROR #define FAN_EVENT_INFO_TYPE_ERROR 5 #endif +#ifndef FAN_EVENT_INFO_TYPE_RANGE +#define FAN_EVENT_INFO_TYPE_RANGE 6 +#endif #ifndef FAN_EVENT_INFO_TYPE_OLD_DFID_NAME #define FAN_EVENT_INFO_TYPE_OLD_DFID_NAME 10 @@ -201,6 +223,15 @@ struct fanotify_event_info_error { }; #endif /* HAVE_STRUCT_FANOTIFY_EVENT_INFO_ERROR */ +#ifndef HAVE_STRUCT_FANOTIFY_EVENT_INFO_RANGE +struct fanotify_event_info_range { + struct fanotify_event_info_header hdr; + __u32 pad; + __u64 offset; + __u64 count; +}; +#endif /* HAVE_STRUCT_FANOTIFY_EVENT_INFO_RANGE */ + /* NOTE: only for struct fanotify_event_info_fid */ #ifdef HAVE_STRUCT_FANOTIFY_EVENT_INFO_FID_FSID___VAL # define FSID_VAL_MEMBER(fsid, i) (fsid.__val[i]) @@ -208,4 +239,9 @@ struct fanotify_event_info_error { # define FSID_VAL_MEMBER(fsid, i) (fsid.val[i]) #endif /* HAVE_STRUCT_FANOTIFY_EVENT_INFO_FID_FSID___VAL */ +/* linux/exportfs.h */ +#ifndef FILEID_INVALID +# define FILEID_INVALID 0xff +#endif + #endif /* LAPI_FANOTIFY_H__ */ diff --git a/include/lapi/fcntl.h b/include/lapi/fcntl.h index cb216e2d..7c050248 100755 --- a/include/lapi/fcntl.h +++ b/include/lapi/fcntl.h @@ -94,6 +94,10 @@ # define AT_REMOVEDIR 0x200 #endif +#ifndef AT_HANDLE_FID +# define AT_HANDLE_FID AT_REMOVEDIR +#endif + #ifndef AT_SYMLINK_FOLLOW # define AT_SYMLINK_FOLLOW 0x400 #endif @@ -150,6 +154,14 @@ # define RENAME_WHITEOUT (1 << 2) #endif +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_CREATED_QUERY +#define F_CREATED_QUERY (F_LINUX_SPECIFIC_BASE + 4) +#endif + /* splice, vmsplice, tee */ #ifndef SPLICE_F_NONBLOCK diff --git a/include/lapi/ficlone.h b/include/lapi/ficlone.h new file mode 100644 index 00000000..22167331 --- /dev/null +++ b/include/lapi/ficlone.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Andrea Cervesato + * Copyright (C) 2024 Cyril Hrubis + */ + +#ifndef LAPI_FICLONE_H__ +#define LAPI_FICLONE_H__ + +#include "config.h" +#include +#include + +#ifndef HAVE_STRUCT_FILE_CLONE_RANGE +struct file_clone_range { + int64_t src_fd; + uint64_t src_offset; + uint64_t src_length; + uint64_t dest_offset; +}; +#endif + +#ifndef FICLONE +# define FICLONE _IOW(0x94, 9, int) +#endif + +#ifndef FICLONERANGE +# define FICLONERANGE _IOW(0x94, 13, struct file_clone_range) +#endif + +#endif /* LAPI_FICLONE_H__ */ diff --git a/include/lapi/fs.h b/include/lapi/fs.h index c19ee821..6b5056bd 100755 --- a/include/lapi/fs.h +++ b/include/lapi/fs.h @@ -10,15 +10,28 @@ #define LAPI_FS_H__ #include "config.h" -#ifndef HAVE_MOUNT_SETATTR -# ifdef HAVE_LINUX_FS_H -# include -# endif +#ifndef HAVE_LINUX_FS +# include #endif +#include +#include #include #include +#include "tst_test.h" #include "lapi/abisize.h" +#include "lapi/syscalls.h" + +#ifndef HAVE_STRUCT_FSXATTR +struct fsxattr { + uint32_t fsx_xflags; /* xflags field value (get/set) */ + uint32_t fsx_extsize; /* extsize field value (get/set)*/ + uint32_t fsx_nextents; /* nextents field value (get) */ + uint32_t fsx_projid; /* project identifier (get/set) */ + uint32_t fsx_cowextsize; /* CoW extsize field value (get/set)*/ + unsigned char fsx_pad[8]; +}; +#endif #ifndef FS_IOC_GETFLAGS # define FS_IOC_GETFLAGS _IOR('f', 1, long) @@ -28,6 +41,14 @@ # define FS_IOC_SETFLAGS _IOW('f', 2, long) #endif +#ifndef FS_IOC_FSGETXATTR +# define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr) +#endif + +#ifndef FS_IOC_FSSETXATTR +# define FS_IOC_FSSETXATTR _IOW('X', 32, struct fsxattr) +#endif + #ifndef FS_COMPR_FL # define FS_COMPR_FL 0x00000004 /* Compress file */ #endif @@ -48,6 +69,18 @@ # define FS_VERITY_FL 0x00100000 /* Verity protected inode */ #endif +#ifndef FS_XFLAG_APPEND +# define FS_XFLAG_APPEND 0x00000010 /* all writes append */ +#endif + +#ifndef FS_XFLAG_EXTSIZE +# define FS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */ +#endif + +#ifndef FS_XFLAG_COWEXTSIZE +# define FS_XFLAG_COWEXTSIZE 0x00010000 /* CoW extent size allocator hint */ +#endif + /* * Helper function to get MAX_LFS_FILESIZE. * Missing PAGE_SHIFT on some libc prevents defining MAX_LFS_FILESIZE. @@ -55,13 +88,13 @@ * 64 bit: macro taken from kernel from include/linux/fs.h * 32 bit: own implementation */ -static inline loff_t tst_max_lfs_filesize(void) +static inline long long tst_max_lfs_filesize(void) { #ifdef TST_ABI64 - return (loff_t)LLONG_MAX; + return LLONG_MAX; #else long page_size = getpagesize(); - loff_t ret = ULONG_MAX; + long long ret = ULONG_MAX; while (page_size >>= 1) ret <<= 1; @@ -70,4 +103,37 @@ static inline loff_t tst_max_lfs_filesize(void) #endif } +#ifndef HAVE_STRUCT_FILE_ATTR +struct file_attr { + uint64_t fa_xflags; /* xflags field value (get/set) */ + uint32_t fa_extsize; /* extsize field value (get/set)*/ + uint32_t fa_nextents; /* nextents field value (get) */ + uint32_t fa_projid; /* project identifier (get/set) */ + uint32_t fa_cowextsize; /* CoW extsize field value (get/set) */ +}; +#endif + +#define FILE_ATTR_SIZE_VER0 24 +#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER0 + +#ifndef HAVE_FILE_GETATTR +static inline int file_getattr(int dfd, const char *filename, + struct file_attr *ufattr, size_t usize, + unsigned int at_flags) +{ + return tst_syscall(__NR_file_getattr, dfd, filename, ufattr, usize, + at_flags); +} +#endif + +#ifndef HAVE_FILE_SETATTR +static inline int file_setattr(int dfd, const char *filename, + struct file_attr *ufattr, size_t usize, + unsigned int at_flags) +{ + return tst_syscall(__NR_file_setattr, dfd, filename, ufattr, usize, + at_flags); +} +#endif + #endif /* LAPI_FS_H__ */ diff --git a/include/lapi/fsmount.h b/include/lapi/fsmount.h index 07eb42ff..1783272a 100755 --- a/include/lapi/fsmount.h +++ b/include/lapi/fsmount.h @@ -11,12 +11,11 @@ #include "config.h" #include #include -#include -#ifndef HAVE_FSOPEN -# ifdef HAVE_LINUX_MOUNT_H -# include -# endif +#if !defined(HAVE_FSOPEN) && defined(HAVE_LINUX_MOUNT_H) +# include +#else +# include #endif #include "lapi/fcntl.h" @@ -119,6 +118,10 @@ static inline int mount_setattr(int dirfd, const char *from_pathname, unsigned i * New headers added in kernel after 5.2 release, create them for old userspace. */ +#ifndef MOVE_MOUNT_BENEATH +# define MOVE_MOUNT_BENEATH 0x00000200 +#endif + #ifndef OPEN_TREE_CLONE /* @@ -136,7 +139,7 @@ static inline int mount_setattr(int dirfd, const char *from_pathname, unsigned i #define MOVE_MOUNT_T_SYMLINKS 0x00000010 /* Follow symlinks on to path */ #define MOVE_MOUNT_T_AUTOMOUNTS 0x00000020 /* Follow automounts on to path */ #define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */ -#define MOVE_MOUNT__MASK 0x00000077 +#define MOVE_MOUNT__MASK 0x00000377 /* * fsopen() flags. diff --git a/include/lapi/getrandom.h b/include/lapi/getrandom.h index c654ca1a..8d5b90ee 100755 --- a/include/lapi/getrandom.h +++ b/include/lapi/getrandom.h @@ -8,10 +8,14 @@ #include "config.h" -#if HAVE_LINUX_RANDOM_H -#include +#ifdef HAVE_SYS_RANDOM_H +# include +#else +# include #endif +#include "lapi/syscalls.h" + /* * Flags for getrandom(2) * @@ -27,4 +31,11 @@ # define GRND_RANDOM 0x0002 #endif +#ifndef HAVE_SYS_RANDOM_H +static inline int getrandom(void *buf, size_t buflen, unsigned int flags) +{ + return tst_syscall(SYS_getrandom, buf, buflen, flags); +} +#endif + #endif /* LAPI_GETRANDOM_H__ */ diff --git a/include/lapi/io_uring.h b/include/lapi/io_uring.h index a63741a0..c0551759 100755 --- a/include/lapi/io_uring.h +++ b/include/lapi/io_uring.h @@ -11,10 +11,9 @@ #include #include -#include #include #include -#include +#include #include "lapi/syscalls.h" diff --git a/include/lapi/ioctl.h b/include/lapi/ioctl.h index f91a9e68..d0f8bf25 100755 --- a/include/lapi/ioctl.h +++ b/include/lapi/ioctl.h @@ -9,6 +9,7 @@ #include "config.h" #include +#include /* musl not including it in */ #include @@ -37,4 +38,136 @@ struct termio }; #endif /* HAVE_STRUCT_TERMIO */ +#ifndef HAVE_STRUCT_PROCMAP_QUERY +#define PROCFS_IOCTL_MAGIC 'f' +#define PROCMAP_QUERY _IOWR(PROCFS_IOCTL_MAGIC, 17, struct procmap_query) +enum procmap_query_flags { + /* + * VMA permission flags. + * + * Can be used as part of procmap_query.query_flags field to look up + * only VMAs satisfying specified subset of permissions. E.g., specifying + * PROCMAP_QUERY_VMA_READABLE only will return both readable and read/write VMAs, + * while having PROCMAP_QUERY_VMA_READABLE | PROCMAP_QUERY_VMA_WRITABLE will only + * return read/write VMAs, though both executable/non-executable and + * private/shared will be ignored. + * + * PROCMAP_QUERY_VMA_* flags are also returned in procmap_query.vma_flags + * field to specify actual VMA permissions. + */ + PROCMAP_QUERY_VMA_READABLE = 0x01, + PROCMAP_QUERY_VMA_WRITABLE = 0x02, + PROCMAP_QUERY_VMA_EXECUTABLE = 0x04, + PROCMAP_QUERY_VMA_SHARED = 0x08, + /* + * Query modifier flags. + * + * By default VMA that covers provided address is returned, or -ENOENT + * is returned. With PROCMAP_QUERY_COVERING_OR_NEXT_VMA flag set, closest + * VMA with vma_start > addr will be returned if no covering VMA is + * found. + * + * PROCMAP_QUERY_FILE_BACKED_VMA instructs query to consider only VMAs that + * have file backing. Can be combined with PROCMAP_QUERY_COVERING_OR_NEXT_VMA + * to iterate all VMAs with file backing. + */ + PROCMAP_QUERY_COVERING_OR_NEXT_VMA = 0x10, + PROCMAP_QUERY_FILE_BACKED_VMA = 0x20, +}; + +struct procmap_query { + /* Query struct size, for backwards/forward compatibility */ + uint64_t size; + /* + * Query flags, a combination of enum procmap_query_flags values. + * Defines query filtering and behavior, see enum procmap_query_flags. + * + * Input argument, provided by user. Kernel doesn't modify it. + */ + uint64_t query_flags; /* in */ + /* + * Query address. By default, VMA that covers this address will + * be looked up. PROCMAP_QUERY_* flags above modify this default + * behavior further. + * + * Input argument, provided by user. Kernel doesn't modify it. + */ + uint64_t query_addr; /* in */ + /* VMA starting (inclusive) and ending (exclusive) address, if VMA is found. */ + uint64_t vma_start; /* out */ + uint64_t vma_end; /* out */ + /* VMA permissions flags. A combination of PROCMAP_QUERY_VMA_* flags. */ + uint64_t vma_flags; /* out */ + /* VMA backing page size granularity. */ + uint64_t vma_page_size; /* out */ + /* + * VMA file offset. If VMA has file backing, this specifies offset + * within the file that VMA's start address corresponds to. + * Is set to zero if VMA has no backing file. + */ + uint64_t vma_offset; /* out */ + /* Backing file's inode number, or zero, if VMA has no backing file. */ + uint64_t inode; /* out */ + /* Backing file's device major/minor number, or zero, if VMA has no backing file. */ + uint32_t dev_major; /* out */ + uint32_t dev_minor; /* out */ + /* + * If set to non-zero value, signals the request to return VMA name + * (i.e., VMA's backing file's absolute path, with " (deleted)" suffix + * appended, if file was unlinked from FS) for matched VMA. VMA name + * can also be some special name (e.g., "[heap]", "[stack]") or could + * be even user-supplied with prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME). + * + * Kernel will set this field to zero, if VMA has no associated name. + * Otherwise kernel will return actual amount of bytes filled in + * user-supplied buffer (see vma_name_addr field below), including the + * terminating zero. + * + * If VMA name is longer that user-supplied maximum buffer size, + * -E2BIG error is returned. + * + * If this field is set to non-zero value, vma_name_addr should point + * to valid user space memory buffer of at least vma_name_size bytes. + * If set to zero, vma_name_addr should be set to zero as well + */ + uint32_t vma_name_size; /* in/out */ + /* + * If set to non-zero value, signals the request to extract and return + * VMA's backing file's build ID, if the backing file is an ELF file + * and it contains embedded build ID. + * + * Kernel will set this field to zero, if VMA has no backing file, + * backing file is not an ELF file, or ELF file has no build ID + * embedded. + * + * Build ID is a binary value (not a string). Kernel will set + * build_id_size field to exact number of bytes used for build ID. + * If build ID is requested and present, but needs more bytes than + * user-supplied maximum buffer size (see build_id_addr field below), + * -E2BIG error will be returned. + * + * If this field is set to non-zero value, build_id_addr should point + * to valid user space memory buffer of at least build_id_size bytes. + * If set to zero, build_id_addr should be set to zero as well + */ + uint32_t build_id_size; /* in/out */ + /* + * User-supplied address of a buffer of at least vma_name_size bytes + * for kernel to fill with matched VMA's name (see vma_name_size field + * description above for details). + * + * Should be set to zero if VMA name should not be returned. + */ + uint64_t vma_name_addr; /* in */ + /* + * User-supplied address of a buffer of at least build_id_size bytes + * for kernel to fill with matched VMA's ELF build ID, if available + * (see build_id_size field description above for details). + * + * Should be set to zero if build ID should not be returned. + */ + uint64_t build_id_addr; /* in */ +}; +#endif /* HAVE_STRUCT_PROCMAP_QUERY */ + #endif /* LAPI_IOCTL_H__ */ diff --git a/include/lapi/ioctl_ns.h b/include/lapi/ioctl_ns.h index 9c81d5ce..37fc5371 100755 --- a/include/lapi/ioctl_ns.h +++ b/include/lapi/ioctl_ns.h @@ -6,6 +6,7 @@ #ifndef LAPI_IOCTL_NS_H__ #define LAPI_IOCTL_NS_H__ +#include #include #ifndef NSIO @@ -23,6 +24,8 @@ #ifndef NS_GET_NSTYPE #define NS_GET_NSTYPE _IO(NSIO, 0x3) #endif - +#ifndef NS_GET_MNTNS_ID +#define NS_GET_MNTNS_ID _IOR(NSIO, 0x5, uint64_t) +#endif #endif /* LAPI_IOCTL_NS_H__ */ diff --git a/include/lapi/keyctl.h b/include/lapi/keyctl.h index 3be78249..e08b8f13 100755 --- a/include/lapi/keyctl.h +++ b/include/lapi/keyctl.h @@ -116,6 +116,10 @@ static inline key_serial_t keyctl_join_session_keyring(const char *name) { # define KEYCTL_SETPERM 5 #endif +#ifndef KEYCTL_DESCRIBE +# define KEYCTL_DESCRIBE 6 +#endif + #ifndef KEYCTL_CLEAR # define KEYCTL_CLEAR 7 #endif @@ -124,6 +128,10 @@ static inline key_serial_t keyctl_join_session_keyring(const char *name) { # define KEYCTL_UNLINK 9 #endif +#ifndef KEYCTL_SEARCH +# define KEYCTL_SEARCH 10 +#endif + #ifndef KEYCTL_READ # define KEYCTL_READ 11 #endif @@ -136,10 +144,26 @@ static inline key_serial_t keyctl_join_session_keyring(const char *name) { # define KEYCTL_SET_TIMEOUT 15 #endif +#ifndef KEYCTL_ASSUME_AUTHORITY +# define KEYCTL_ASSUME_AUTHORITY 16 +#endif + +#ifndef KEYCTL_GET_SECURITY +# define KEYCTL_GET_SECURITY 17 +#endif + #ifndef KEYCTL_INVALIDATE # define KEYCTL_INVALIDATE 21 #endif +#ifndef KEYCTL_GET_PERSISTENT +# define KEYCTL_GET_PERSISTENT 22 +#endif + +#ifndef KEYCTL_DH_COMPUTE +# define KEYCTL_DH_COMPUTE 23 +#endif + #ifndef KEYCTL_WATCH_KEY # define KEYCTL_WATCH_KEY 32 #endif @@ -179,4 +203,53 @@ static inline key_serial_t keyctl_join_session_keyring(const char *name) { # define KEY_OTH_ALL 0x0000003f #endif /* !KEY_POS_VIEW */ +static inline long safe_keyctl(const char *file, const int lineno, + int cmd, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + long rval; + int failure = 0; + + rval = keyctl(cmd, arg2, arg3, arg4, arg5); + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "keyctl(%d, %lu, %lu, %lu, %lu)", + cmd, arg2, arg3, arg4, arg5); + } + + switch (cmd) { + case KEYCTL_GET_KEYRING_ID: + case KEYCTL_JOIN_SESSION_KEYRING: + case KEYCTL_DESCRIBE: + case KEYCTL_SEARCH: + case KEYCTL_READ: + case KEYCTL_SET_REQKEY_KEYRING: + case KEYCTL_GET_SECURITY: + case KEYCTL_GET_PERSISTENT: + case KEYCTL_DH_COMPUTE: + if (rval < 0) + failure = 1; + break; + case KEYCTL_ASSUME_AUTHORITY: + if ((!arg2 && rval) || (arg2 && rval < 0)) + failure = 1; + break; + default: + if (rval) + failure = 1; + break; + } + + if (failure) { + tst_brk_(file, lineno, TBROK, + "keyctl(%d, %lu, %lu, %lu, %lu) returned %ld", + cmd, arg2, arg3, arg4, arg5, rval); + } + + return rval; +} +#define SAFE_KEYCTL(cmd, arg2, arg3, arg4, arg5) \ + safe_keyctl(__FILE__, __LINE__, \ + (cmd), (arg2), (arg3), (arg4), (arg5)) + #endif /* LAPI_KEYCTL_H__ */ diff --git a/include/lapi/landlock.h b/include/lapi/landlock.h new file mode 100644 index 00000000..e579500e --- /dev/null +++ b/include/lapi/landlock.h @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef LAPI_LANDLOCK_H__ +#define LAPI_LANDLOCK_H__ + +#include "config.h" +#include + +#ifdef HAVE_LINUX_LANDLOCK_H +# include +#endif + +#include "lapi/syscalls.h" + +struct tst_landlock_ruleset_attr_abi1 { + uint64_t handled_access_fs; +}; + +struct tst_landlock_ruleset_attr_abi4 { + uint64_t handled_access_fs; + uint64_t handled_access_net; +}; + +struct tst_landlock_ruleset_attr_abi6 { + uint64_t handled_access_fs; + uint64_t handled_access_net; + uint64_t scoped; +}; + +#ifndef HAVE_STRUCT_LANDLOCK_PATH_BENEATH_ATTR +struct landlock_path_beneath_attr +{ + uint64_t allowed_access; + int32_t parent_fd; +} __attribute__((packed)); +#endif + +#if !HAVE_DECL_LANDLOCK_RULE_PATH_BENEATH +# define LANDLOCK_RULE_PATH_BENEATH 1 +#endif + +#if !HAVE_DECL_LANDLOCK_RULE_NET_PORT +# define LANDLOCK_RULE_NET_PORT 2 +#endif + +#ifndef HAVE_STRUCT_LANDLOCK_NET_PORT_ATTR +struct landlock_net_port_attr { + uint64_t allowed_access; + uint64_t port; +}; +#endif + +#ifndef LANDLOCK_CREATE_RULESET_VERSION +# define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) +#endif + +#ifndef LANDLOCK_ACCESS_FS_EXECUTE +# define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0) +#endif + +#ifndef LANDLOCK_ACCESS_FS_WRITE_FILE +# define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1) +#endif + +#ifndef LANDLOCK_ACCESS_FS_READ_FILE +# define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) +#endif + +#ifndef LANDLOCK_ACCESS_FS_READ_DIR +# define LANDLOCK_ACCESS_FS_READ_DIR (1ULL << 3) +#endif + +#ifndef LANDLOCK_ACCESS_FS_REMOVE_DIR +# define LANDLOCK_ACCESS_FS_REMOVE_DIR (1ULL << 4) +#endif + +#ifndef LANDLOCK_ACCESS_FS_REMOVE_FILE +# define LANDLOCK_ACCESS_FS_REMOVE_FILE (1ULL << 5) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_CHAR +# define LANDLOCK_ACCESS_FS_MAKE_CHAR (1ULL << 6) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_DIR +# define LANDLOCK_ACCESS_FS_MAKE_DIR (1ULL << 7) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_REG +# define LANDLOCK_ACCESS_FS_MAKE_REG (1ULL << 8) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_SOCK +# define LANDLOCK_ACCESS_FS_MAKE_SOCK (1ULL << 9) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_FIFO +# define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_BLOCK +# define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_SYM +# define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) +#endif + +#ifndef LANDLOCK_ACCESS_FS_REFER +# define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) +#endif + +#ifndef LANDLOCK_ACCESS_FS_TRUNCATE +# define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) +#endif + +#ifndef LANDLOCK_ACCESS_FS_IOCTL_DEV +# define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15) +#endif + +#ifndef LANDLOCK_ACCESS_NET_BIND_TCP +# define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0) +#endif + +#ifndef LANDLOCK_ACCESS_NET_CONNECT_TCP +# define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1) +#endif + +#ifndef LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET +# define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0) +#endif + +#ifndef LANDLOCK_SCOPE_SIGNAL +# define LANDLOCK_SCOPE_SIGNAL (1ULL << 1) +#endif + +static inline int safe_landlock_create_ruleset(const char *file, const int lineno, + const void *attr, size_t size , uint32_t flags) +{ + int rval; + + rval = tst_syscall(__NR_landlock_create_ruleset, attr, size, flags); + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "landlock_create_ruleset(%p, %zi, %u)", + attr, size, flags); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid landlock_create_ruleset(%p, %lu, %u) return value %d", + attr, size, flags, rval); + } + + return rval; +} + +static inline int safe_landlock_add_rule(const char *file, const int lineno, + int ruleset_fd, int rule_type, const void *rule_attr, uint32_t flags) +{ + int rval; + + rval = tst_syscall(__NR_landlock_add_rule, + ruleset_fd, rule_type, rule_attr, flags); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "landlock_add_rule(%d, %d, %p, %u)", + ruleset_fd, rule_type, rule_attr, flags); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid landlock_add_rule(%d, %d, %p, %u) return value %d", + ruleset_fd, rule_type, rule_attr, flags, rval); + } + + return rval; +} + +static inline int safe_landlock_restrict_self(const char *file, const int lineno, + int ruleset_fd, int flags) +{ + int rval; + + rval = tst_syscall(__NR_landlock_restrict_self, ruleset_fd, flags); + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "landlock_restrict_self(%d, %u)", + ruleset_fd, flags); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid landlock_restrict_self(%d, %u) return value %d", + ruleset_fd, flags, rval); + } + + return rval; +} + +#define SAFE_LANDLOCK_CREATE_RULESET(attr, size, flags) \ + safe_landlock_create_ruleset(__FILE__, __LINE__, (attr), (size), (flags)) + +#define SAFE_LANDLOCK_ADD_RULE(ruleset_fd, rule_type, rule_attr, flags) \ + safe_landlock_add_rule(__FILE__, __LINE__, \ + (ruleset_fd), (rule_type), (rule_attr), (flags)) + +#define SAFE_LANDLOCK_RESTRICT_SELF(ruleset_fd, flags) \ + safe_landlock_restrict_self(__FILE__, __LINE__, (ruleset_fd), (flags)) + +#endif diff --git a/include/lapi/ldt.h b/include/lapi/ldt.h new file mode 100644 index 00000000..d9233d09 --- /dev/null +++ b/include/lapi/ldt.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +#ifndef LAPI_LDT_H__ +#define LAPI_LDT_H__ + +#include "config.h" +#include "lapi/syscalls.h" + +#ifdef HAVE_ASM_LDT_H +#include +#else +struct user_desc { + unsigned int entry_number; + unsigned int base_addr; + unsigned int limit; + unsigned int seg_32bit : 1; + unsigned int contents : 2; + unsigned int read_exec_only : 1; + unsigned int limit_in_pages : 1; + unsigned int seg_not_present : 1; + unsigned int useable : 1; +#ifdef __x86_64__ + unsigned int lm : 1; +#endif /* __x86_64__ */ +}; +#endif /* HAVE_ASM_LDT_H */ + +static inline int modify_ldt(int func, const struct user_desc *ptr, + unsigned long bytecount) +{ + long rval; + + errno = 0; + rval = tst_syscall(__NR_modify_ldt, func, ptr, bytecount); + +#ifdef __x86_64__ + /* + * The kernel intentionally casts modify_ldt() return value + * to unsigned int to prevent sign extension to 64 bits. This may + * result in syscall() returning the value as is instead of setting + * errno and returning -1. + */ + if (rval > 0 && (int)rval < 0) { + tst_res(TINFO, + "WARNING: Libc mishandled modify_ldt() return value"); + errno = -(int)rval; + rval = -1; + } +#endif /* __x86_64__ */ + + return rval; +} + +static inline int safe_modify_ldt(const char *file, const int lineno, int func, + const struct user_desc *ptr, + unsigned long bytecount) +{ + int rval; + + rval = modify_ldt(func, ptr, bytecount); + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "modify_ldt(%d, %p, %lu)", func, ptr, bytecount); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "modify_ltd(%d, %p, %lu) invalid retval %i", func, ptr, + bytecount, rval); + } + + return rval; +} + +#define SAFE_MODIFY_LDT(func, ptr, bytecount) \ + safe_modify_ldt(__FILE__, __LINE__, (func), (ptr), (bytecount)) + +static inline int set_thread_area(const struct user_desc *u_info) +{ + return tst_syscall(__NR_set_thread_area, u_info); +} + +static inline int get_thread_area(const struct user_desc *u_info) +{ + return tst_syscall(__NR_get_thread_area, u_info); +} + +#endif /* LAPI_LDT_H__ */ diff --git a/include/lapi/lsm.h b/include/lapi/lsm.h new file mode 100644 index 00000000..72ca85f7 --- /dev/null +++ b/include/lapi/lsm.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef LAPI_LSM_H__ +#define LAPI_LSM_H__ + +#include "config.h" + +#ifdef HAVE_LINUX_LSM_H +#include +#endif + +#include +#include "lapi/syscalls.h" + +#define CTX_DATA_SIZE 4096 + +#define LSM_CTX_SIZE(x) (sizeof(struct lsm_ctx) + x) +#define LSM_CTX_SIZE_DEFAULT LSM_CTX_SIZE(CTX_DATA_SIZE) + +#ifndef HAVE_STRUCT_LSM_CTX + +/** + * struct lsm_ctx - LSM context information + * @id: the LSM id number, see LSM_ID_XXX + * @flags: LSM specific flags + * @len: length of the lsm_ctx struct, @ctx and any other data or padding + * @ctx_len: the size of @ctx + * @ctx: the LSM context value + * + * The @len field MUST be equal to the size of the lsm_ctx struct + * plus any additional padding and/or data placed after @ctx. + * + * In all cases @ctx_len MUST be equal to the length of @ctx. + * If @ctx is a string value it should be nul terminated with + * @ctx_len equal to `strlen(@ctx) + 1`. Binary values are + * supported. + * + * The @flags and @ctx fields SHOULD only be interpreted by the + * LSM specified by @id; they MUST be set to zero/0 when not used. + */ +struct lsm_ctx { + uint64_t id; + uint64_t flags; + uint64_t len; + uint64_t ctx_len; + uint8_t ctx[]; +}; +#endif + +/* + * ID tokens to identify Linux Security Modules (LSMs) + * + * These token values are used to uniquely identify specific LSMs + * in the kernel as well as in the kernel's LSM userspace API. + */ +#ifndef LSM_ID_UNDEF +# define LSM_ID_UNDEF 0 +#endif + +#ifndef LSM_ID_CAPABILITY +# define LSM_ID_CAPABILITY 100 +#endif + +#ifndef LSM_ID_SELINUX +# define LSM_ID_SELINUX 101 +#endif + +#ifndef LSM_ID_SMACK +# define LSM_ID_SMACK 102 +#endif + +#ifndef LSM_ID_TOMOYO +# define LSM_ID_TOMOYO 103 +#endif + +#ifndef LSM_ID_APPARMOR +# define LSM_ID_APPARMOR 104 +#endif + +#ifndef LSM_ID_YAMA +# define LSM_ID_YAMA 105 +#endif + +#ifndef LSM_ID_LOADPIN +# define LSM_ID_LOADPIN 106 +#endif + +#ifndef LSM_ID_SAFESETID +# define LSM_ID_SAFESETID 107 +#endif + +#ifndef LSM_ID_LOCKDOWN +# define LSM_ID_LOCKDOWN 108 +#endif + +#ifndef LSM_ID_BPF +# define LSM_ID_BPF 109 +#endif + +#ifndef LSM_ID_LANDLOCK +# define LSM_ID_LANDLOCK 110 +#endif + +#ifndef LSM_ID_IMA +# define LSM_ID_IMA 111 +#endif + +#ifndef LSM_ID_EVM +# define LSM_ID_EVM 112 +#endif + +#ifndef LSM_ID_IPE +# define LSM_ID_IPE 113 +#endif + +/* + * LSM_ATTR_XXX definitions identify different LSM attributes + * which are used in the kernel's LSM userspace API. Support + * for these attributes vary across the different LSMs. None + * are required. + */ +#ifndef LSM_ATTR_UNDEF +# define LSM_ATTR_UNDEF 0 +#endif + +#ifndef LSM_ATTR_CURRENT +# define LSM_ATTR_CURRENT 100 +#endif + +#ifndef LSM_ATTR_EXEC +# define LSM_ATTR_EXEC 101 +#endif + +#ifndef LSM_ATTR_FSCREATE +# define LSM_ATTR_FSCREATE 102 +#endif + +#ifndef LSM_ATTR_KEYCREATE +# define LSM_ATTR_KEYCREATE 103 +#endif + +#ifndef LSM_ATTR_PREV +# define LSM_ATTR_PREV 104 +#endif + +#ifndef LSM_ATTR_SOCKCREATE +# define LSM_ATTR_SOCKCREATE 105 +#endif + +/* + * LSM_FLAG_XXX definitions identify special handling instructions + * for the API. + */ +#ifndef LSM_FLAG_SINGLE +# define LSM_FLAG_SINGLE 0x0001 +#endif + +static inline int lsm_get_self_attr(uint32_t attr, struct lsm_ctx *ctx, + uint32_t *size, uint32_t flags) +{ + return tst_syscall(__NR_lsm_get_self_attr, attr, ctx, size, flags); +} + +static inline int lsm_set_self_attr(uint32_t attr, struct lsm_ctx *ctx, + uint32_t size, uint32_t flags) +{ + return tst_syscall(__NR_lsm_set_self_attr, attr, ctx, size, flags); +} + +static inline int lsm_list_modules(uint64_t *ids, uint32_t *size, uint32_t flags) +{ + return tst_syscall(__NR_lsm_list_modules, ids, size, flags); +} +#endif diff --git a/include/lapi/mkdirat.h b/include/lapi/mkdirat.h deleted file mode 100755 index 72eb7f64..00000000 --- a/include/lapi/mkdirat.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2014 Cyril Hrubis - */ - -#ifndef LAPI_MKDIRAT_H__ -#define LAPI_MKDIRAT_H__ - -#include "config.h" -#include "lapi/syscalls.h" -#include "lapi/fcntl.h" - -#ifndef HAVE_MKDIRAT -static inline int mkdirat(int dirfd, const char *dirname, int mode) -{ - return tst_syscall(__NR_mkdirat, dirfd, dirname, mode); -} -#endif - -#endif /* LAPI_MKDIRAT_H__ */ diff --git a/include/lapi/mman.h b/include/lapi/mman.h new file mode 100644 index 00000000..edd517c6 --- /dev/null +++ b/include/lapi/mman.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef LAPI_MMAN_H__ +#define LAPI_MMAN_H__ + +#include +#include +#include "config.h" +#include "lapi/syscalls.h" + +#ifndef HAVE_STRUCT_CACHESTAT_RANGE +struct cachestat_range { + uint64_t off; + uint64_t len; +}; +#endif + +#ifndef HAVE_STRUCT_CACHESTAT +struct cachestat { + uint64_t nr_cache; + uint64_t nr_dirty; + uint64_t nr_writeback; + uint64_t nr_evicted; + uint64_t nr_recently_evicted; +}; +#endif + +#ifndef HAVE_CACHESTAT +/* + * cachestat: wrapper function of cachestat + * + * Returns: It returns status of cachestat syscall + */ +static inline int cachestat(int fd, struct cachestat_range *cstat_range, + struct cachestat *cstat, unsigned int flags) +{ + return tst_syscall(__NR_cachestat, fd, cstat_range, cstat, flags); +} +#endif + +#endif /* LAPI_MMAN_H__ */ diff --git a/include/lapi/mmap.h b/include/lapi/mmap.h index 7512e9f8..248b6456 100755 --- a/include/lapi/mmap.h +++ b/include/lapi/mmap.h @@ -38,6 +38,14 @@ # define MADV_SOFT_OFFLINE 101 #endif +#ifndef MADV_GUARD_INSTALL +# define MADV_GUARD_INSTALL 102 +#endif + +#ifndef MADV_GUARD_REMOVE +# define MADV_GUARD_REMOVE 103 +#endif + #ifndef MADV_MERGEABLE # define MADV_MERGEABLE 12 #endif @@ -79,6 +87,10 @@ # define MADV_PAGEOUT 21 #endif +#ifndef MAP_DROPPABLE +# define MAP_DROPPABLE 0x08 +#endif + #ifndef MAP_FIXED_NOREPLACE #ifdef __alpha__ diff --git a/include/lapi/mount.h b/include/lapi/mount.h index c1af944f..0f7bb5e4 100755 --- a/include/lapi/mount.h +++ b/include/lapi/mount.h @@ -1,12 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) Linux Test Project, 2015-2022 + * Copyright (c) Linux Test Project, 2015-2025 * Copyright (c) 2015 Cui Bixuan + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ #ifndef LAPI_MOUNT_H__ #define LAPI_MOUNT_H__ +#include "config.h" +#include + +/* + * NOTE: conflicts with , therefore not added + * although some definitions from it are used. + */ #include #ifndef MS_REC @@ -37,4 +45,87 @@ # define MS_NOSYMFOLLOW 256 #endif +#ifndef HAVE_STRUCT_MNT_ID_REQ +struct mnt_id_req { + uint32_t size; + uint32_t spare; + uint64_t mnt_id; + uint64_t param; + uint64_t mnt_ns_id; +}; +#endif + +#ifndef HAVE_STRUCT_STATMOUNT +struct statmount { + uint32_t size; + uint32_t __spare1; + uint64_t mask; + uint32_t sb_dev_major; + uint32_t sb_dev_minor; + uint64_t sb_magic; + uint32_t sb_flags; + uint32_t fs_type; + uint64_t mnt_id; + uint64_t mnt_parent_id; + uint32_t mnt_id_old; + uint32_t mnt_parent_id_old; + uint64_t mnt_attr; + uint64_t mnt_propagation; + uint64_t mnt_peer_group; + uint64_t mnt_master; + uint64_t propagate_from; + uint32_t mnt_root; + uint32_t mnt_point; + uint64_t mnt_ns_id; + uint32_t fs_subtype; + uint32_t sb_source; + uint32_t opt_num; + uint32_t opt_array; + uint32_t opt_sec_num; + uint32_t opt_sec_array; + uint32_t mnt_uidmap_num; + uint32_t mnt_uidmap; + uint32_t mnt_gidmap_num; + uint32_t mnt_gidmap; + uint64_t __spare2[44]; + char str[]; +}; +#endif + +#ifndef MNT_ID_REQ_SIZE_VER0 +# define MNT_ID_REQ_SIZE_VER0 24 +#endif + +#ifndef STATMOUNT_SB_BASIC +# define STATMOUNT_SB_BASIC 0x00000001U +#endif + +#ifndef STATMOUNT_MNT_BASIC +# define STATMOUNT_MNT_BASIC 0x00000002U +#endif + +#ifndef STATMOUNT_PROPAGATE_FROM +# define STATMOUNT_PROPAGATE_FROM 0x00000004U +#endif + +#ifndef STATMOUNT_MNT_ROOT +# define STATMOUNT_MNT_ROOT 0x00000008U +#endif + +#ifndef STATMOUNT_MNT_POINT +# define STATMOUNT_MNT_POINT 0x00000010U +#endif + +#ifndef STATMOUNT_FS_TYPE +# define STATMOUNT_FS_TYPE 0x00000020U +#endif + +#ifndef STATMOUNT_MNT_NS_ID +# define STATMOUNT_MNT_NS_ID 0x00000040U +#endif + +#ifndef LSMT_ROOT +# define LSMT_ROOT 0xffffffffffffffff +#endif + #endif /* LAPI_MOUNT_H__ */ diff --git a/include/lapi/nf_tables.h b/include/lapi/nf_tables.h new file mode 100644 index 00000000..9feb3a60 --- /dev/null +++ b/include/lapi/nf_tables.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023 SUSE LLC + */ + +#ifndef LAPI_NF_TABLES_H__ +#define LAPI_NF_TABLES_H__ + +#include + +#ifndef HAVE_DECL_NFTA_CHAIN_ID +# define NFTA_CHAIN_ID 11 +#endif + +#ifndef HAVE_DECL_NFTA_VERDICT_CHAIN_ID +# define NFTA_VERDICT_CHAIN_ID 3 +#endif + +#endif /* LAPI_NF_TABLES_H__ */ diff --git a/include/lapi/pidfd.h b/include/lapi/pidfd.h index 9ca8e5aa..a3205032 100644 --- a/include/lapi/pidfd.h +++ b/include/lapi/pidfd.h @@ -8,14 +8,51 @@ #define LAPI_PIDFD_H__ #include +#include +#include + #ifdef HAVE_SYS_PIDFD_H # include #endif + #include "config.h" #include "lapi/syscalls.h" +#ifndef HAVE_STRUCT_PIDFD_INFO +struct pidfd_info { + uint64_t mask; + uint64_t cgroupid; + uint32_t pid; + uint32_t tgid; + uint32_t ppid; + uint32_t ruid; + uint32_t rgid; + uint32_t euid; + uint32_t egid; + uint32_t suid; + uint32_t sgid; + uint32_t fsuid; + uint32_t fsgid; + int32_t exit_code; + uint32_t coredump_mask; + uint32_t __spare1; +}; +#endif + #ifndef PIDFD_NONBLOCK -#define PIDFD_NONBLOCK O_NONBLOCK +# define PIDFD_NONBLOCK O_NONBLOCK +#endif + +#ifndef PIDFS_IOCTL_MAGIC +# define PIDFS_IOCTL_MAGIC 0xFF +#endif + +#ifndef PIDFD_GET_INFO +# define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info) +#endif + +#ifndef PIDFD_INFO_EXIT +# define PIDFD_INFO_EXIT (1UL << 3) #endif static inline void pidfd_send_signal_supported(void) diff --git a/testcases/kernel/syscalls/pkeys/pkey.h b/include/lapi/pkey.h old mode 100755 new mode 100644 similarity index 61% rename from testcases/kernel/syscalls/pkeys/pkey.h rename to include/lapi/pkey.h index 6e32326b..eb9bf7fb --- a/testcases/kernel/syscalls/pkeys/pkey.h +++ b/include/lapi/pkey.h @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2019 Red Hat, Inc. - * Copyright (c) Linux Test Project, 2019 + * Copyright (c) 2019-2024 Red Hat, Inc. + * Copyright (c) Linux Test Project, 2019-2024 */ -#ifndef PKEYS_H -#define PKEYS_H +#ifndef PKEYS_H__ +#define PKEYS_H__ +#include "config.h" #include "tst_test.h" #include "lapi/syscalls.h" #include "lapi/mmap.h" @@ -16,30 +17,30 @@ # define PKEY_DISABLE_WRITE 0x2 #endif +#ifndef PKEY_DISABLE_EXECUTE +# define PKEY_DISABLE_EXECUTE 0x4 +#endif + #ifndef HAVE_PKEY_MPROTECT -inline int ltp_pkey_mprotect(void *addr, size_t len, int prot, int pkey) +inline int pkey_mprotect(void *addr, size_t len, int prot, int pkey) { return tst_syscall(__NR_pkey_mprotect, addr, len, prot, pkey); } -inline int ltp_pkey_alloc(unsigned int flags, unsigned int access_rights) +inline int pkey_alloc(unsigned int flags, unsigned int access_rights) { return tst_syscall(__NR_pkey_alloc, flags, access_rights); } -inline int ltp_pkey_free(int pkey) +inline int pkey_free(int pkey) { return tst_syscall(__NR_pkey_free, pkey); } -#else -#define ltp_pkey_alloc pkey_alloc -#define ltp_pkey_free pkey_free -#define ltp_pkey_mprotect pkey_mprotect #endif /* HAVE_PKEY_MPROTECT */ static inline void check_pkey_support(void) { - int pkey = ltp_pkey_alloc(0, 0); + int pkey = tst_syscall(__NR_pkey_alloc, 0, 0); if (pkey == -1) { if (errno == ENOSYS) @@ -50,7 +51,7 @@ static inline void check_pkey_support(void) tst_brk(TCONF, "pkeys are not available for test"); } - ltp_pkey_free(pkey); + tst_syscall(__NR_pkey_free, pkey); } -#endif /* PKEYS_H */ +#endif /* PKEYS_H__ */ diff --git a/include/lapi/pwritev2.h b/include/lapi/pwritev2.h deleted file mode 100755 index 48b53f46..00000000 --- a/include/lapi/pwritev2.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. - * Author: Jinhui Huang - */ - -#ifndef LAPI_PWRITEV2_H__ -#define LAPI_PWRITEV2_H__ - -#include "config.h" -#include "lapi/syscalls.h" - -#if !defined(HAVE_PWRITEV2) - -/* LO_HI_LONG taken from glibc */ -# define LO_HI_LONG(val) (long) (val), (long) (((uint64_t) (val)) >> 32) - -static inline ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt, - off_t offset, int flags) -{ - return tst_syscall(__NR_pwritev2, fd, iov, iovcnt, - LO_HI_LONG(offset), flags); -} -#endif - -#endif /* LAPI_PWRITEV2_H__ */ diff --git a/include/lapi/readlinkat.h b/include/lapi/readlinkat.h deleted file mode 100755 index a680deee..00000000 --- a/include/lapi/readlinkat.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2014 Cyril Hrubis - */ - -#ifndef LAPI_READLINKAT_H__ -#define LAPI_READLINKAT_H__ - -#include "config.h" -#include "lapi/syscalls.h" -#include "lapi/fcntl.h" - -#ifndef HAVE_READLINKAT -static inline int readlinkat(int dirfd, const char *pathname, - char *buf, size_t bufsiz) -{ - return tst_syscall(__NR_readlinkat, dirfd, pathname, buf, bufsiz); -} -#endif - -#endif /* LAPI_READLINKAT_H__ */ diff --git a/include/lapi/renameat.h b/include/lapi/renameat.h deleted file mode 100755 index abf4c1d6..00000000 --- a/include/lapi/renameat.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) International Business Machines Corp., 2007 - * Copyright (c) 2014 Fujitsu Ltd. - */ - -#ifndef LAPI_RENAMEAT_H__ -#define LAPI_RENAMEAT_H__ - -#include -#include "config.h" -#include "lapi/syscalls.h" - -#if !defined(HAVE_RENAMEAT) -static inline int renameat(int olddirfd, const char *oldpath, int newdirfd, - const char *newpath) -{ - return tst_syscall(__NR_renameat, olddirfd, oldpath, newdirfd, - newpath); -} -#endif - -#endif /* LAPI_RENAMEAT_H__ */ diff --git a/include/lapi/resource.h b/include/lapi/resource.h new file mode 100644 index 00000000..fec03d83 --- /dev/null +++ b/include/lapi/resource.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Red Hat Inc. All Rights Reserved. + * Author: Chunfu Wen + */ + +#ifndef LAPI_RESOURCE_H__ +#define LAPI_RESOURCE_H__ + +#define _LARGEFILE64_SOURCE + +#include +#include "config.h" +#include "lapi/syscalls.h" + +#ifndef HAVE_STRUCT_RLIMIT64 +struct rlimit64 { + uint64_t rlim_cur; + uint64_t rlim_max; +}; +#endif + +static int setrlimit_u64(int resource, struct rlimit64 *rlim) +{ + return tst_syscall(__NR_prlimit64, 0, resource, rlim, NULL); +} + +#endif /* LAPI_RESOURCE_H__ */ diff --git a/include/lapi/sched.h b/include/lapi/sched.h index 26fdb628..36f1ecad 100755 --- a/include/lapi/sched.h +++ b/include/lapi/sched.h @@ -13,8 +13,9 @@ #include #include "config.h" #include "lapi/syscalls.h" -#include "lapi/sched.h" +/* sched_attr is not defined in glibc < 2.41 */ +#ifndef SCHED_ATTR_SIZE_VER0 struct sched_attr { uint32_t size; @@ -45,6 +46,9 @@ static inline int sched_getattr(pid_t pid, struct sched_attr *attr, return syscall(__NR_sched_getattr, pid, attr, size, flags); } +# define SCHED_ATTR_SIZE_VER0 48 /* sizeof first published struct */ +#endif + #ifndef HAVE_CLONE3 struct clone_args { uint64_t __attribute__((aligned(8))) flags; diff --git a/include/lapi/socket.h b/include/lapi/socket.h index 794dee49..23e7ba6c 100755 --- a/include/lapi/socket.h +++ b/include/lapi/socket.h @@ -62,6 +62,10 @@ # define SOL_ALG 279 #endif +#ifndef SOL_TLS +# define SOL_TLS 282 +#endif + #ifndef HAVE_STRUCT_MMSGHDR struct mmsghdr { struct msghdr msg_hdr; diff --git a/include/lapi/stat.h b/include/lapi/stat.h index 3606c9eb..17b62ea9 100755 --- a/include/lapi/stat.h +++ b/include/lapi/stat.h @@ -30,6 +30,7 @@ struct statx_timestamp { int32_t __reserved; }; #endif + /* * Structures for the extended file attribute retrieval system call * (statx()). @@ -67,39 +68,53 @@ struct statx_timestamp { * will have values installed for compatibility purposes so that stat() and * co. can be emulated in userspace. */ -#ifndef HAVE_STRUCT_STATX -struct statx { - /* 0x00 */ - uint32_t stx_mask; - uint32_t stx_blksize; - uint64_t stx_attributes; - /* 0x10 */ - uint32_t stx_nlink; - uint32_t stx_uid; - uint32_t stx_gid; - uint16_t stx_mode; - uint16_t __spare0[1]; - /* 0x20 */ - uint64_t stx_ino; - uint64_t stx_size; - uint64_t stx_blocks; - uint64_t stx_attributes_mask; - /* 0x40 */ - const struct statx_timestamp stx_atime; - const struct statx_timestamp stx_btime; - const struct statx_timestamp stx_ctime; - const struct statx_timestamp stx_mtime; - /* 0x80 */ - uint32_t stx_rdev_major; - uint32_t stx_rdev_minor; - uint32_t stx_dev_major; - uint32_t stx_dev_minor; - /* 0x90 */ - uint64_t __spare2[14]; - /* 0x100 */ + #define LTP_DEFINE_STATX_STRUCT(x) \ + struct x { \ + uint32_t stx_mask; \ + uint32_t stx_blksize; \ + uint64_t stx_attributes; \ + uint32_t stx_nlink; \ + uint32_t stx_uid; \ + uint32_t stx_gid; \ + uint16_t stx_mode; \ + uint16_t __spare0[1]; \ + uint64_t stx_ino; \ + uint64_t stx_size; \ + uint64_t stx_blocks; \ + uint64_t stx_attributes_mask; \ + const struct statx_timestamp stx_atime; \ + const struct statx_timestamp stx_btime; \ + const struct statx_timestamp stx_ctime; \ + const struct statx_timestamp stx_mtime; \ + uint32_t stx_rdev_major; \ + uint32_t stx_rdev_minor; \ + uint32_t stx_dev_major; \ + uint32_t stx_dev_minor; \ + uint64_t stx_mnt_id; \ + uint32_t stx_dio_mem_align; \ + uint32_t stx_dio_offset_align; \ + uint64_t __spare3[12]; \ }; + +LTP_DEFINE_STATX_STRUCT(statx_fallback); + +#ifndef HAVE_STRUCT_STATX +LTP_DEFINE_STATX_STRUCT(statx); #endif +/* + * This is the fallback statx that we pass to the safe_statx() syscall. + * The reason why we need it, is that statx struct is constantly changing + * inside the kernel and we need to extend its definition when structure + * changes in order to compile the tests. + */ +struct ltp_statx { + union { + struct statx buff; + struct statx_fallback data; + }; +}; + #ifndef HAVE_STATX /* @@ -108,9 +123,9 @@ struct statx { * Returns: It returns status of statx syscall */ static inline int statx(int dirfd, const char *pathname, unsigned int flags, - unsigned int mask, struct statx *statxbuf) + unsigned int mask, struct statx *st) { - return tst_syscall(__NR_statx, dirfd, pathname, flags, mask, statxbuf); + return tst_syscall(__NR_statx, dirfd, pathname, flags, mask, st); } #endif @@ -229,4 +244,54 @@ static inline int statx(int dirfd, const char *pathname, unsigned int flags, # define STATX_ATTR_VERITY 0x00100000 #endif +#ifndef STATX_MNT_ID_UNIQUE +# define STATX_MNT_ID_UNIQUE 0x00004000U +#endif + +#define SAFE_FCHMODAT2(dfd, filename, mode, flags) \ + safe_fchmodat2(__FILE__, __LINE__, (dfd), (filename), (mode), (flags)) + +static inline int safe_fchmodat2(const char *file, const int lineno, + int dfd, const char *filename, mode_t mode, int flags) +{ + int ret; + + ret = tst_syscall(__NR_fchmodat2, dfd, filename, mode, flags); + if (ret == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "syscall(__NR_fchmodat2,%d,%s,%d,%d) failed", + dfd, filename, mode, flags); + } else if (ret) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid syscall(__NR_fchmodat2,%d,%s,%d,%d) return value %d", + dfd, filename, mode, flags, ret); + } + + return ret; +} + +#define SAFE_STATX(dirfd, pathname, flags, mask, buf) \ + safe_statx(__FILE__, __LINE__, (dirfd), (pathname), (flags), (mask), (buf)) + +static inline int safe_statx(const char *file, const int lineno, + int dirfd, const char *pathname, int flags, unsigned int mask, + struct ltp_statx *buf) +{ + int rval; + + rval = statx(dirfd, pathname, flags, mask, &buf->buff); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "statx(%d,%s,%d,%u,%p) failed", dirfd, pathname, flags, mask, buf); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid statx(%d,%s,%d,%u,%p) return value %d", + dirfd, pathname, flags, mask, buf, + rval); + } + + return rval; +} + #endif /* LAPI_STAT_H__ */ diff --git a/include/lapi/syscalls/arc.in b/include/lapi/syscalls/arc.in index 3e2ee906..0f0fbef6 100755 --- a/include/lapi/syscalls/arc.in +++ b/include/lapi/syscalls/arc.in @@ -23,7 +23,7 @@ epoll_ctl 21 epoll_pwait 22 dup 23 dup3 24 -fcntl 25 +fcntl64 25 inotify_init1 26 inotify_add_watch 27 inotify_rm_watch 28 @@ -41,10 +41,10 @@ umount2 39 mount 40 pivot_root 41 nfsservctl 42 -statfs 43 -fstatfs 44 -truncate 45 -ftruncate 46 +statfs64 43 +fstatfs64 44 +truncate64 45 +ftruncate64 46 fallocate 47 faccessat 48 chdir 49 @@ -60,7 +60,7 @@ vhangup 58 pipe2 59 quotactl 60 getdents64 61 -lseek 62 +llseek 62 read 63 write 64 readv 65 @@ -69,7 +69,7 @@ pread64 67 pwrite64 68 preadv 69 pwritev 70 -sendfile 71 +sendfile64 71 pselect6 72 ppoll 73 signalfd4 74 @@ -77,12 +77,11 @@ vmsplice 75 splice 76 tee 77 readlinkat 78 -fstatat 79 -fstat 80 +fstatat64 79 +fstat64 80 sync 81 fsync 82 fdatasync 83 -sync_file_range2 84 sync_file_range 84 timerfd_create 85 timerfd_settime 86 @@ -221,8 +220,8 @@ request_key 218 keyctl 219 clone 220 execve 221 -mmap 222 -fadvise64 223 +mmap2 222 +fadvise64_64 223 swapon 224 swapoff 225 mprotect 226 @@ -261,6 +260,11 @@ sendmmsg 269 process_vm_readv 270 process_vm_writev 271 kcmp 272 +finit_module 273 +sched_setattr 274 +sched_getattr 275 +renameat2 276 +seccomp 277 getrandom 278 memfd_create 279 bpf 280 @@ -314,6 +318,32 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/arm.in b/include/lapi/syscalls/arm.in index 7bdbca53..d0238dec 100755 --- a/include/lapi/syscalls/arm.in +++ b/include/lapi/syscalls/arm.in @@ -1,397 +1,422 @@ -restart_syscall (__NR_SYSCALL_BASE+ 0) -exit (__NR_SYSCALL_BASE+ 1) -fork (__NR_SYSCALL_BASE+ 2) -read (__NR_SYSCALL_BASE+ 3) -write (__NR_SYSCALL_BASE+ 4) -open (__NR_SYSCALL_BASE+ 5) -close (__NR_SYSCALL_BASE+ 6) -creat (__NR_SYSCALL_BASE+ 8) -link (__NR_SYSCALL_BASE+ 9) -unlink (__NR_SYSCALL_BASE+ 10) -execve (__NR_SYSCALL_BASE+ 11) -chdir (__NR_SYSCALL_BASE+ 12) -mknod (__NR_SYSCALL_BASE+ 14) -chmod (__NR_SYSCALL_BASE+ 15) -lchown (__NR_SYSCALL_BASE+ 16) -lseek (__NR_SYSCALL_BASE+ 19) -getpid (__NR_SYSCALL_BASE+ 20) -mount (__NR_SYSCALL_BASE+ 21) -setuid (__NR_SYSCALL_BASE+ 23) -getuid (__NR_SYSCALL_BASE+ 24) -ptrace (__NR_SYSCALL_BASE+ 26) -pause (__NR_SYSCALL_BASE+ 29) -access (__NR_SYSCALL_BASE+ 33) -nice (__NR_SYSCALL_BASE+ 34) -sync (__NR_SYSCALL_BASE+ 36) -kill (__NR_SYSCALL_BASE+ 37) -rename (__NR_SYSCALL_BASE+ 38) -mkdir (__NR_SYSCALL_BASE+ 39) -rmdir (__NR_SYSCALL_BASE+ 40) -dup (__NR_SYSCALL_BASE+ 41) -pipe (__NR_SYSCALL_BASE+ 42) -times (__NR_SYSCALL_BASE+ 43) -brk (__NR_SYSCALL_BASE+ 45) -setgid (__NR_SYSCALL_BASE+ 46) -getgid (__NR_SYSCALL_BASE+ 47) -geteuid (__NR_SYSCALL_BASE+ 49) -getegid (__NR_SYSCALL_BASE+ 50) -acct (__NR_SYSCALL_BASE+ 51) -umount2 (__NR_SYSCALL_BASE+ 52) -ioctl (__NR_SYSCALL_BASE+ 54) -fcntl (__NR_SYSCALL_BASE+ 55) -setpgid (__NR_SYSCALL_BASE+ 57) -umask (__NR_SYSCALL_BASE+ 60) -chroot (__NR_SYSCALL_BASE+ 61) -ustat (__NR_SYSCALL_BASE+ 62) -dup2 (__NR_SYSCALL_BASE+ 63) -getppid (__NR_SYSCALL_BASE+ 64) -getpgrp (__NR_SYSCALL_BASE+ 65) -setsid (__NR_SYSCALL_BASE+ 66) -sigaction (__NR_SYSCALL_BASE+ 67) -setreuid (__NR_SYSCALL_BASE+ 70) -setregid (__NR_SYSCALL_BASE+ 71) -sigsuspend (__NR_SYSCALL_BASE+ 72) -sigpending (__NR_SYSCALL_BASE+ 73) -sethostname (__NR_SYSCALL_BASE+ 74) -setrlimit (__NR_SYSCALL_BASE+ 75) -getrusage (__NR_SYSCALL_BASE+ 77) -gettimeofday (__NR_SYSCALL_BASE+ 78) -settimeofday (__NR_SYSCALL_BASE+ 79) -getgroups (__NR_SYSCALL_BASE+ 80) -setgroups (__NR_SYSCALL_BASE+ 81) -symlink (__NR_SYSCALL_BASE+ 83) -readlink (__NR_SYSCALL_BASE+ 85) -uselib (__NR_SYSCALL_BASE+ 86) -swapon (__NR_SYSCALL_BASE+ 87) -reboot (__NR_SYSCALL_BASE+ 88) -munmap (__NR_SYSCALL_BASE+ 91) -truncate (__NR_SYSCALL_BASE+ 92) -ftruncate (__NR_SYSCALL_BASE+ 93) -fchmod (__NR_SYSCALL_BASE+ 94) -fchown (__NR_SYSCALL_BASE+ 95) -getpriority (__NR_SYSCALL_BASE+ 96) -setpriority (__NR_SYSCALL_BASE+ 97) -statfs (__NR_SYSCALL_BASE+ 99) -fstatfs (__NR_SYSCALL_BASE+100) -syslog (__NR_SYSCALL_BASE+103) -setitimer (__NR_SYSCALL_BASE+104) -getitimer (__NR_SYSCALL_BASE+105) -stat (__NR_SYSCALL_BASE+106) -lstat (__NR_SYSCALL_BASE+107) -fstat (__NR_SYSCALL_BASE+108) -vhangup (__NR_SYSCALL_BASE+111) -wait4 (__NR_SYSCALL_BASE+114) -swapoff (__NR_SYSCALL_BASE+115) -sysinfo (__NR_SYSCALL_BASE+116) -fsync (__NR_SYSCALL_BASE+118) -sigreturn (__NR_SYSCALL_BASE+119) -clone (__NR_SYSCALL_BASE+120) -setdomainname (__NR_SYSCALL_BASE+121) -uname (__NR_SYSCALL_BASE+122) -adjtimex (__NR_SYSCALL_BASE+124) -mprotect (__NR_SYSCALL_BASE+125) -sigprocmask (__NR_SYSCALL_BASE+126) -init_module (__NR_SYSCALL_BASE+128) -delete_module (__NR_SYSCALL_BASE+129) -quotactl (__NR_SYSCALL_BASE+131) -getpgid (__NR_SYSCALL_BASE+132) -fchdir (__NR_SYSCALL_BASE+133) -bdflush (__NR_SYSCALL_BASE+134) -sysfs (__NR_SYSCALL_BASE+135) -personality (__NR_SYSCALL_BASE+136) -setfsuid (__NR_SYSCALL_BASE+138) -setfsgid (__NR_SYSCALL_BASE+139) -_llseek (__NR_SYSCALL_BASE+140) -getdents (__NR_SYSCALL_BASE+141) -_newselect (__NR_SYSCALL_BASE+142) -flock (__NR_SYSCALL_BASE+143) -msync (__NR_SYSCALL_BASE+144) -readv (__NR_SYSCALL_BASE+145) -writev (__NR_SYSCALL_BASE+146) -getsid (__NR_SYSCALL_BASE+147) -fdatasync (__NR_SYSCALL_BASE+148) -_sysctl (__NR_SYSCALL_BASE+149) -mlock (__NR_SYSCALL_BASE+150) -munlock (__NR_SYSCALL_BASE+151) -mlockall (__NR_SYSCALL_BASE+152) -munlockall (__NR_SYSCALL_BASE+153) -sched_setparam (__NR_SYSCALL_BASE+154) -sched_getparam (__NR_SYSCALL_BASE+155) -sched_setscheduler (__NR_SYSCALL_BASE+156) -sched_getscheduler (__NR_SYSCALL_BASE+157) -sched_yield (__NR_SYSCALL_BASE+158) -sched_get_priority_max (__NR_SYSCALL_BASE+159) -sched_get_priority_min (__NR_SYSCALL_BASE+160) -sched_rr_get_interval (__NR_SYSCALL_BASE+161) -nanosleep (__NR_SYSCALL_BASE+162) -mremap (__NR_SYSCALL_BASE+163) -setresuid (__NR_SYSCALL_BASE+164) -getresuid (__NR_SYSCALL_BASE+165) -poll (__NR_SYSCALL_BASE+168) -nfsservctl (__NR_SYSCALL_BASE+169) -setresgid (__NR_SYSCALL_BASE+170) -getresgid (__NR_SYSCALL_BASE+171) -prctl (__NR_SYSCALL_BASE+172) -rt_sigreturn (__NR_SYSCALL_BASE+173) -rt_sigaction (__NR_SYSCALL_BASE+174) -rt_sigprocmask (__NR_SYSCALL_BASE+175) -rt_sigpending (__NR_SYSCALL_BASE+176) -rt_sigtimedwait (__NR_SYSCALL_BASE+177) -rt_sigqueueinfo (__NR_SYSCALL_BASE+178) -rt_sigsuspend (__NR_SYSCALL_BASE+179) -pread64 (__NR_SYSCALL_BASE+180) -pwrite64 (__NR_SYSCALL_BASE+181) -chown (__NR_SYSCALL_BASE+182) -getcwd (__NR_SYSCALL_BASE+183) -capget (__NR_SYSCALL_BASE+184) -capset (__NR_SYSCALL_BASE+185) -sigaltstack (__NR_SYSCALL_BASE+186) -sendfile (__NR_SYSCALL_BASE+187) -vfork (__NR_SYSCALL_BASE+190) -ugetrlimit (__NR_SYSCALL_BASE+191) -mmap2 (__NR_SYSCALL_BASE+192) -truncate64 (__NR_SYSCALL_BASE+193) -ftruncate64 (__NR_SYSCALL_BASE+194) -stat64 (__NR_SYSCALL_BASE+195) -lstat64 (__NR_SYSCALL_BASE+196) -fstat64 (__NR_SYSCALL_BASE+197) -lchown32 (__NR_SYSCALL_BASE+198) -getuid32 (__NR_SYSCALL_BASE+199) -getgid32 (__NR_SYSCALL_BASE+200) -geteuid32 (__NR_SYSCALL_BASE+201) -getegid32 (__NR_SYSCALL_BASE+202) -setreuid32 (__NR_SYSCALL_BASE+203) -setregid32 (__NR_SYSCALL_BASE+204) -getgroups32 (__NR_SYSCALL_BASE+205) -setgroups32 (__NR_SYSCALL_BASE+206) -fchown32 (__NR_SYSCALL_BASE+207) -setresuid32 (__NR_SYSCALL_BASE+208) -getresuid32 (__NR_SYSCALL_BASE+209) -setresgid32 (__NR_SYSCALL_BASE+210) -getresgid32 (__NR_SYSCALL_BASE+211) -chown32 (__NR_SYSCALL_BASE+212) -setuid32 (__NR_SYSCALL_BASE+213) -setgid32 (__NR_SYSCALL_BASE+214) -setfsuid32 (__NR_SYSCALL_BASE+215) -setfsgid32 (__NR_SYSCALL_BASE+216) -getdents64 (__NR_SYSCALL_BASE+217) -pivot_root (__NR_SYSCALL_BASE+218) -mincore (__NR_SYSCALL_BASE+219) -madvise (__NR_SYSCALL_BASE+220) -fcntl64 (__NR_SYSCALL_BASE+221) -gettid (__NR_SYSCALL_BASE+224) -readahead (__NR_SYSCALL_BASE+225) -setxattr (__NR_SYSCALL_BASE+226) -lsetxattr (__NR_SYSCALL_BASE+227) -fsetxattr (__NR_SYSCALL_BASE+228) -getxattr (__NR_SYSCALL_BASE+229) -lgetxattr (__NR_SYSCALL_BASE+230) -fgetxattr (__NR_SYSCALL_BASE+231) -listxattr (__NR_SYSCALL_BASE+232) -llistxattr (__NR_SYSCALL_BASE+233) -flistxattr (__NR_SYSCALL_BASE+234) -removexattr (__NR_SYSCALL_BASE+235) -lremovexattr (__NR_SYSCALL_BASE+236) -fremovexattr (__NR_SYSCALL_BASE+237) -tkill (__NR_SYSCALL_BASE+238) -sendfile64 (__NR_SYSCALL_BASE+239) -futex (__NR_SYSCALL_BASE+240) -sched_setaffinity (__NR_SYSCALL_BASE+241) -sched_getaffinity (__NR_SYSCALL_BASE+242) -io_setup (__NR_SYSCALL_BASE+243) -io_destroy (__NR_SYSCALL_BASE+244) -io_getevents (__NR_SYSCALL_BASE+245) -io_submit (__NR_SYSCALL_BASE+246) -io_cancel (__NR_SYSCALL_BASE+247) -exit_group (__NR_SYSCALL_BASE+248) -lookup_dcookie (__NR_SYSCALL_BASE+249) -epoll_create (__NR_SYSCALL_BASE+250) -epoll_ctl (__NR_SYSCALL_BASE+251) -epoll_wait (__NR_SYSCALL_BASE+252) -remap_file_pages (__NR_SYSCALL_BASE+253) -set_tid_address (__NR_SYSCALL_BASE+256) -timer_create (__NR_SYSCALL_BASE+257) -timer_settime (__NR_SYSCALL_BASE+258) -timer_gettime (__NR_SYSCALL_BASE+259) -timer_getoverrun (__NR_SYSCALL_BASE+260) -timer_delete (__NR_SYSCALL_BASE+261) -clock_settime (__NR_SYSCALL_BASE+262) -clock_gettime (__NR_SYSCALL_BASE+263) -clock_getres (__NR_SYSCALL_BASE+264) -clock_nanosleep (__NR_SYSCALL_BASE+265) -statfs64 (__NR_SYSCALL_BASE+266) -fstatfs64 (__NR_SYSCALL_BASE+267) -tgkill (__NR_SYSCALL_BASE+268) -utimes (__NR_SYSCALL_BASE+269) -arm_fadvise64_64 (__NR_SYSCALL_BASE+270) -pciconfig_iobase (__NR_SYSCALL_BASE+271) -pciconfig_read (__NR_SYSCALL_BASE+272) -pciconfig_write (__NR_SYSCALL_BASE+273) -mq_open (__NR_SYSCALL_BASE+274) -mq_unlink (__NR_SYSCALL_BASE+275) -mq_timedsend (__NR_SYSCALL_BASE+276) -mq_timedreceive (__NR_SYSCALL_BASE+277) -mq_notify (__NR_SYSCALL_BASE+278) -mq_getsetattr (__NR_SYSCALL_BASE+279) -waitid (__NR_SYSCALL_BASE+280) -socket (__NR_SYSCALL_BASE+281) -bind (__NR_SYSCALL_BASE+282) -connect (__NR_SYSCALL_BASE+283) -listen (__NR_SYSCALL_BASE+284) -accept (__NR_SYSCALL_BASE+285) -getsockname (__NR_SYSCALL_BASE+286) -getpeername (__NR_SYSCALL_BASE+287) -socketpair (__NR_SYSCALL_BASE+288) -send (__NR_SYSCALL_BASE+289) -sendto (__NR_SYSCALL_BASE+290) -recv (__NR_SYSCALL_BASE+291) -recvfrom (__NR_SYSCALL_BASE+292) -shutdown (__NR_SYSCALL_BASE+293) -setsockopt (__NR_SYSCALL_BASE+294) -getsockopt (__NR_SYSCALL_BASE+295) -sendmsg (__NR_SYSCALL_BASE+296) -recvmsg (__NR_SYSCALL_BASE+297) -semop (__NR_SYSCALL_BASE+298) -semget (__NR_SYSCALL_BASE+299) -semctl (__NR_SYSCALL_BASE+300) -msgsnd (__NR_SYSCALL_BASE+301) -msgrcv (__NR_SYSCALL_BASE+302) -msgget (__NR_SYSCALL_BASE+303) -msgctl (__NR_SYSCALL_BASE+304) -shmat (__NR_SYSCALL_BASE+305) -shmdt (__NR_SYSCALL_BASE+306) -shmget (__NR_SYSCALL_BASE+307) -shmctl (__NR_SYSCALL_BASE+308) -add_key (__NR_SYSCALL_BASE+309) -request_key (__NR_SYSCALL_BASE+310) -keyctl (__NR_SYSCALL_BASE+311) -semtimedop (__NR_SYSCALL_BASE+312) -vserver (__NR_SYSCALL_BASE+313) -ioprio_set (__NR_SYSCALL_BASE+314) -ioprio_get (__NR_SYSCALL_BASE+315) -inotify_init (__NR_SYSCALL_BASE+316) -inotify_add_watch (__NR_SYSCALL_BASE+317) -inotify_rm_watch (__NR_SYSCALL_BASE+318) -mbind (__NR_SYSCALL_BASE+319) -get_mempolicy (__NR_SYSCALL_BASE+320) -set_mempolicy (__NR_SYSCALL_BASE+321) -openat (__NR_SYSCALL_BASE+322) -mkdirat (__NR_SYSCALL_BASE+323) -mknodat (__NR_SYSCALL_BASE+324) -fchownat (__NR_SYSCALL_BASE+325) -futimesat (__NR_SYSCALL_BASE+326) -fstatat64 (__NR_SYSCALL_BASE+327) -unlinkat (__NR_SYSCALL_BASE+328) -renameat (__NR_SYSCALL_BASE+329) -linkat (__NR_SYSCALL_BASE+330) -symlinkat (__NR_SYSCALL_BASE+331) -readlinkat (__NR_SYSCALL_BASE+332) -fchmodat (__NR_SYSCALL_BASE+333) -faccessat (__NR_SYSCALL_BASE+334) -pselect6 (__NR_SYSCALL_BASE+335) -ppoll (__NR_SYSCALL_BASE+336) -unshare (__NR_SYSCALL_BASE+337) -set_robust_list (__NR_SYSCALL_BASE+338) -get_robust_list (__NR_SYSCALL_BASE+339) -splice (__NR_SYSCALL_BASE+340) -arm_sync_file_range (__NR_SYSCALL_BASE+341) -sync_file_range2 __NR_arm_sync_file_range -tee (__NR_SYSCALL_BASE+342) -vmsplice (__NR_SYSCALL_BASE+343) -move_pages (__NR_SYSCALL_BASE+344) -getcpu (__NR_SYSCALL_BASE+345) -epoll_pwait (__NR_SYSCALL_BASE+346) -kexec_load (__NR_SYSCALL_BASE+347) -utimensat (__NR_SYSCALL_BASE+348) -signalfd (__NR_SYSCALL_BASE+349) -timerfd_create (__NR_SYSCALL_BASE+350) -eventfd (__NR_SYSCALL_BASE+351) -fallocate (__NR_SYSCALL_BASE+352) -timerfd_settime (__NR_SYSCALL_BASE+353) -timerfd_gettime (__NR_SYSCALL_BASE+354) -signalfd4 (__NR_SYSCALL_BASE+355) -eventfd2 (__NR_SYSCALL_BASE+356) -epoll_create1 (__NR_SYSCALL_BASE+357) -dup3 (__NR_SYSCALL_BASE+358) -pipe2 (__NR_SYSCALL_BASE+359) -inotify_init1 (__NR_SYSCALL_BASE+360) -preadv (__NR_SYSCALL_BASE+361) -pwritev (__NR_SYSCALL_BASE+362) -rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363) -perf_event_open (__NR_SYSCALL_BASE+364) -recvmmsg (__NR_SYSCALL_BASE+365) -accept4 (__NR_SYSCALL_BASE+366) -fanotify_init (__NR_SYSCALL_BASE+367) -fanotify_mark (__NR_SYSCALL_BASE+368) -prlimit64 (__NR_SYSCALL_BASE+369) -name_to_handle_at (__NR_SYSCALL_BASE+370) -open_by_handle_at (__NR_SYSCALL_BASE+371) -clock_adjtime (__NR_SYSCALL_BASE+372) -syncfs (__NR_SYSCALL_BASE+373) -sendmmsg (__NR_SYSCALL_BASE+374) -setns (__NR_SYSCALL_BASE+375) -process_vm_readv (__NR_SYSCALL_BASE+376) -process_vm_writev (__NR_SYSCALL_BASE+377) -kcmp (__NR_SYSCALL_BASE+378) -finit_module (__NR_SYSCALL_BASE+379) -sched_setattr (__NR_SYSCALL_BASE+380) -sched_getattr (__NR_SYSCALL_BASE+381) -renameat2 (__NR_SYSCALL_BASE+382) -seccomp (__NR_SYSCALL_BASE+383) -getrandom (__NR_SYSCALL_BASE+384) -memfd_create (__NR_SYSCALL_BASE+385) -bpf (__NR_SYSCALL_BASE+386) -execveat (__NR_SYSCALL_BASE+387) -userfaultfd (__NR_SYSCALL_BASE+388) -membarrier (__NR_SYSCALL_BASE+389) -mlock2 (__NR_SYSCALL_BASE+390) -copy_file_range (__NR_SYSCALL_BASE+391) -preadv2 (__NR_SYSCALL_BASE+392) -pwritev2 (__NR_SYSCALL_BASE+393) -pkey_mprotect (__NR_SYSCALL_BASE+394) -pkey_alloc (__NR_SYSCALL_BASE+395) -pkey_free (__NR_SYSCALL_BASE+396) -statx (__NR_SYSCALL_BASE+397) -rseq (__NR_SYSCALL_BASE+398) -io_pgetevents (__NR_SYSCALL_BASE+399) -migrate_pages (__NR_SYSCALL_BASE+400) -kexec_file_load (__NR_SYSCALL_BASE+401) -clock_gettime64 (__NR_SYSCALL_BASE+403) -clock_settime64 (__NR_SYSCALL_BASE+404) -clock_adjtime64 (__NR_SYSCALL_BASE+405) -clock_getres_time64 (__NR_SYSCALL_BASE+406) -clock_nanosleep_time64 (__NR_SYSCALL_BASE+407) -timer_gettime64 (__NR_SYSCALL_BASE+408) -timer_settime64 (__NR_SYSCALL_BASE+409) -timerfd_gettime64 (__NR_SYSCALL_BASE+410) -timerfd_settime64 (__NR_SYSCALL_BASE+411) -utimensat_time64 (__NR_SYSCALL_BASE+412) -pselect6_time64 (__NR_SYSCALL_BASE+413) -ppoll_time64 (__NR_SYSCALL_BASE+414) -io_pgetevents_time64 (__NR_SYSCALL_BASE+416) -recvmmsg_time64 (__NR_SYSCALL_BASE+417) -mq_timedsend_time64 (__NR_SYSCALL_BASE+418) -mq_timedreceive_time64 (__NR_SYSCALL_BASE+419) -semtimedop_time64 (__NR_SYSCALL_BASE+420) -rt_sigtimedwait_time64 (__NR_SYSCALL_BASE+421) -futex_time64 (__NR_SYSCALL_BASE+422) -sched_rr_get_interval_time64 (__NR_SYSCALL_BASE+423) -pidfd_send_signal (__NR_SYSCALL_BASE+424) -io_uring_setup (__NR_SYSCALL_BASE+425) -io_uring_enter (__NR_SYSCALL_BASE+426) -io_uring_register (__NR_SYSCALL_BASE+427) -open_tree (__NR_SYSCALL_BASE+428) -move_mount (__NR_SYSCALL_BASE+429) -fsopen (__NR_SYSCALL_BASE+430) -fsconfig (__NR_SYSCALL_BASE+431) -fsmount (__NR_SYSCALL_BASE+432) -fspick (__NR_SYSCALL_BASE+433) -pidfd_open (__NR_SYSCALL_BASE+434) -clone3 (__NR_SYSCALL_BASE+435) -close_range (__NR_SYSCALL_BASE+436) -openat2 (__NR_SYSCALL_BASE+437) -pidfd_getfd (__NR_SYSCALL_BASE+438) -faccessat2 (__NR_SYSCALL_BASE+439) -epoll_pwait2 (__NR_SYSCALL_BASE+441) -quotactl_fd (__NR_SYSCALL_BASE+443) -futex_waitv (__NR_SYSCALL_BASE+449) +restart_syscall 0 +exit 1 +fork 2 +read 3 +write 4 +open 5 +close 6 +creat 8 +link 9 +unlink 10 +execve 11 +chdir 12 +mknod 14 +chmod 15 +lchown 16 +lseek 19 +getpid 20 +mount 21 +setuid 23 +getuid 24 +ptrace 26 +pause 29 +access 33 +nice 34 +sync 36 +kill 37 +rename 38 +mkdir 39 +rmdir 40 +dup 41 +pipe 42 +times 43 +brk 45 +setgid 46 +getgid 47 +geteuid 49 +getegid 50 +acct 51 +umount2 52 +ioctl 54 +fcntl 55 +setpgid 57 +umask 60 +chroot 61 +ustat 62 +dup2 63 +getppid 64 +getpgrp 65 +setsid 66 +sigaction 67 +setreuid 70 +setregid 71 +sigsuspend 72 +sigpending 73 +sethostname 74 +setrlimit 75 +getrusage 77 +gettimeofday 78 +settimeofday 79 +getgroups 80 +setgroups 81 +symlink 83 +readlink 85 +uselib 86 +swapon 87 +reboot 88 +munmap 91 +truncate 92 +ftruncate 93 +fchmod 94 +fchown 95 +getpriority 96 +setpriority 97 +statfs 99 +fstatfs 100 +syslog 103 +setitimer 104 +getitimer 105 +stat 106 +lstat 107 +fstat 108 +vhangup 111 +wait4 114 +swapoff 115 +sysinfo 116 +fsync 118 +sigreturn 119 +clone 120 +setdomainname 121 +uname 122 +adjtimex 124 +mprotect 125 +sigprocmask 126 +init_module 128 +delete_module 129 +quotactl 131 +getpgid 132 +fchdir 133 +sysfs 135 +personality 136 +setfsuid 138 +setfsgid 139 +_llseek 140 +getdents 141 +_newselect 142 +flock 143 +msync 144 +readv 145 +writev 146 +getsid 147 +fdatasync 148 +_sysctl 149 +mlock 150 +munlock 151 +mlockall 152 +munlockall 153 +sched_setparam 154 +sched_getparam 155 +sched_setscheduler 156 +sched_getscheduler 157 +sched_yield 158 +sched_get_priority_max 159 +sched_get_priority_min 160 +sched_rr_get_interval 161 +nanosleep 162 +mremap 163 +setresuid 164 +getresuid 165 +poll 168 +nfsservctl 169 +setresgid 170 +getresgid 171 +prctl 172 +rt_sigreturn 173 +rt_sigaction 174 +rt_sigprocmask 175 +rt_sigpending 176 +rt_sigtimedwait 177 +rt_sigqueueinfo 178 +rt_sigsuspend 179 +pread64 180 +pwrite64 181 +chown 182 +getcwd 183 +capget 184 +capset 185 +sigaltstack 186 +sendfile 187 +vfork 190 +ugetrlimit 191 +mmap2 192 +truncate64 193 +ftruncate64 194 +stat64 195 +lstat64 196 +fstat64 197 +lchown32 198 +getuid32 199 +getgid32 200 +geteuid32 201 +getegid32 202 +setreuid32 203 +setregid32 204 +getgroups32 205 +setgroups32 206 +fchown32 207 +setresuid32 208 +getresuid32 209 +setresgid32 210 +getresgid32 211 +chown32 212 +setuid32 213 +setgid32 214 +setfsuid32 215 +setfsgid32 216 +getdents64 217 +pivot_root 218 +mincore 219 +madvise 220 +fcntl64 221 +gettid 224 +readahead 225 +setxattr 226 +lsetxattr 227 +fsetxattr 228 +getxattr 229 +lgetxattr 230 +fgetxattr 231 +listxattr 232 +llistxattr 233 +flistxattr 234 +removexattr 235 +lremovexattr 236 +fremovexattr 237 +tkill 238 +sendfile64 239 +futex 240 +sched_setaffinity 241 +sched_getaffinity 242 +io_setup 243 +io_destroy 244 +io_getevents 245 +io_submit 246 +io_cancel 247 +exit_group 248 +lookup_dcookie 249 +epoll_create 250 +epoll_ctl 251 +epoll_wait 252 +remap_file_pages 253 +set_tid_address 256 +timer_create 257 +timer_settime 258 +timer_gettime 259 +timer_getoverrun 260 +timer_delete 261 +clock_settime 262 +clock_gettime 263 +clock_getres 264 +clock_nanosleep 265 +statfs64 266 +fstatfs64 267 +tgkill 268 +utimes 269 +arm_fadvise64_64 270 +pciconfig_iobase 271 +pciconfig_read 272 +pciconfig_write 273 +mq_open 274 +mq_unlink 275 +mq_timedsend 276 +mq_timedreceive 277 +mq_notify 278 +mq_getsetattr 279 +waitid 280 +socket 281 +bind 282 +connect 283 +listen 284 +accept 285 +getsockname 286 +getpeername 287 +socketpair 288 +send 289 +sendto 290 +recv 291 +recvfrom 292 +shutdown 293 +setsockopt 294 +getsockopt 295 +sendmsg 296 +recvmsg 297 +semop 298 +semget 299 +semctl 300 +msgsnd 301 +msgrcv 302 +msgget 303 +msgctl 304 +shmat 305 +shmdt 306 +shmget 307 +shmctl 308 +add_key 309 +request_key 310 +keyctl 311 +semtimedop 312 +vserver 313 +ioprio_set 314 +ioprio_get 315 +inotify_init 316 +inotify_add_watch 317 +inotify_rm_watch 318 +mbind 319 +get_mempolicy 320 +set_mempolicy 321 +openat 322 +mkdirat 323 +mknodat 324 +fchownat 325 +futimesat 326 +fstatat64 327 +unlinkat 328 +renameat 329 +linkat 330 +symlinkat 331 +readlinkat 332 +fchmodat 333 +faccessat 334 +pselect6 335 +ppoll 336 +unshare 337 +set_robust_list 338 +get_robust_list 339 +splice 340 +arm_sync_file_range 341 +sync_file_range2 341 +tee 342 +vmsplice 343 +move_pages 344 +getcpu 345 +epoll_pwait 346 +kexec_load 347 +utimensat 348 +signalfd 349 +timerfd_create 350 +eventfd 351 +fallocate 352 +timerfd_settime 353 +timerfd_gettime 354 +signalfd4 355 +eventfd2 356 +epoll_create1 357 +dup3 358 +pipe2 359 +inotify_init1 360 +preadv 361 +pwritev 362 +rt_tgsigqueueinfo 363 +perf_event_open 364 +recvmmsg 365 +accept4 366 +fanotify_init 367 +fanotify_mark 368 +prlimit64 369 +name_to_handle_at 370 +open_by_handle_at 371 +clock_adjtime 372 +syncfs 373 +sendmmsg 374 +setns 375 +process_vm_readv 376 +process_vm_writev 377 +kcmp 378 +finit_module 379 +sched_setattr 380 +sched_getattr 381 +renameat2 382 +seccomp 383 +getrandom 384 +memfd_create 385 +bpf 386 +execveat 387 +userfaultfd 388 +membarrier 389 +mlock2 390 +copy_file_range 391 +preadv2 392 +pwritev2 393 +pkey_mprotect 394 +pkey_alloc 395 +pkey_free 396 +statx 397 +rseq 398 +io_pgetevents 399 +migrate_pages 400 +kexec_file_load 401 +clock_gettime64 403 +clock_settime64 404 +clock_adjtime64 405 +clock_getres_time64 406 +clock_nanosleep_time64 407 +timer_gettime64 408 +timer_settime64 409 +timerfd_gettime64 410 +timerfd_settime64 411 +utimensat_time64 412 +pselect6_time64 413 +ppoll_time64 414 +io_pgetevents_time64 416 +recvmmsg_time64 417 +mq_timedsend_time64 418 +mq_timedreceive_time64 419 +semtimedop_time64 420 +rt_sigtimedwait_time64 421 +futex_time64 422 +sched_rr_get_interval_time64 423 +pidfd_send_signal 424 +io_uring_setup 425 +io_uring_enter 426 +io_uring_register 427 +open_tree 428 +move_mount 429 +fsopen 430 +fsconfig 431 +fsmount 432 +fspick 433 +pidfd_open 434 +clone3 435 +close_range 436 +openat2 437 +pidfd_getfd 438 +faccessat2 439 +process_madvise 440 +epoll_pwait2 441 +mount_setattr 442 +quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 +futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/loongarch.in b/include/lapi/syscalls/arm64.in similarity index 92% rename from include/lapi/syscalls/loongarch.in rename to include/lapi/syscalls/arm64.in index 301f611f..c76930e2 100644 --- a/include/lapi/syscalls/loongarch.in +++ b/include/lapi/syscalls/arm64.in @@ -77,12 +77,11 @@ vmsplice 75 splice 76 tee 77 readlinkat 78 -fstatat 79 +newfstatat 79 fstat 80 sync 81 fsync 82 fdatasync 83 -sync_file_range2 84 sync_file_range 84 timerfd_create 85 timerfd_settime 86 @@ -305,3 +304,22 @@ memfd_secret 447 process_mrelease 448 futex_waitv 449 set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/generate_arch.sh b/include/lapi/syscalls/generate_arch.sh new file mode 100644 index 00000000..5d731794 --- /dev/null +++ b/include/lapi/syscalls/generate_arch.sh @@ -0,0 +1,213 @@ +#!/bin/sh -eu +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2009-2024 +# Copyright (c) Marcin Juszkiewicz, 2023-2024 +# +# This is an adaptation of the update-tables.sh script, included in the +# syscalls-table project (https://github.com/hrw/syscalls-table) and released +# under the MIT license. +# +# Author: Andrea Cervesato + +if [ "$#" -eq "0" ]; then + echo "Please provide kernel sources:" + echo "" + echo "$0 path/to/Linux/kernel/sources" + echo "" + exit 1 +fi + +KERNELSRC="$1" + +# to keep sorting in order +export LC_ALL=C + +if [ ! -d "${KERNELSRC}" ]; then + echo "${KERNELSRC} is not a directory" + exit 1 +fi + +if [ ! -e "${KERNELSRC}/Makefile" ]; then + echo "No Makefile in ${KERNELSRC} directory" + exit 1 +fi + +TEMP="$(mktemp -d)" +KVER="$(make -C ${KERNELSRC} kernelversion -s)" + +SCRIPT_DIR="$(realpath $(dirname "$0"))" +SUPPORTED_ARCH="${SCRIPT_DIR}/supported-arch.txt" +LINUX_HEADERS="${TEMP}/headers" + +grab_syscall_names_from_tables() { + for tbl_file in $(find ${KERNELSRC}/arch -name syscall*.tbl); do + grep -E -v "(^#|^$|sys_ni_syscall)" $tbl_file | + awk '{ print $3 }' >>${TEMP}/syscall-names.tosort + done + + drop_bad_entries +} + +grab_syscall_names_from_unistd_h() { + grep -E -h "^#define __NR_" \ + ${LINUX_HEADERS}/usr/include/asm/unistd*.h \ + ${LINUX_HEADERS}/usr/include/asm-generic/unistd.h \ + >${TEMP}/syscall-names.tosort + + drop_bad_entries +} + +drop_bad_entries() { + grep -E -v "(unistd.h|NR3264|__NR_syscall|__SC_COMP|__NR_.*Linux|__NR_FAST)" \ + ${TEMP}/syscall-names.tosort | + grep -E -v "(__SYSCALL|SYSCALL_BASE|SYSCALL_MASK)" | + sed -e "s/#define\s*__NR_//g" -e "s/\s.*//g" | + sort -u >${TEMP}/syscall-names.txt +} + +generate_table() { + echo "- $arch" + + if [ "$bits" -eq "32" ]; then + extraflags="${extraflags} -D__BITS_PER_LONG=32" + fi + + local uppercase_arch=$(echo "$arch" | tr '[:lower:]' '[:upper:]') + + # ignore any error generated by gcc. We want to obtain all the + # available architecture syscalls for the current platform and to handle + # only supported architectures later on + gcc ${TEMP}/list-syscalls.c -U__LP64__ -U__ILP32__ -U__i386__ \ + -D${uppercase_arch} \ + -D__${arch}__ ${extraflags} \ + -I ${LINUX_HEADERS}/usr/include/ \ + -o ${TEMP}/list-syscalls || true + + ${TEMP}/list-syscalls >"${TEMP}/${arch}.in.tosort" + + sort -k2,2n "${TEMP}/${arch}.in.tosort" >"${TEMP}/${arch}.in" +} + +generate_list_syscalls_c() { + ( + printf " + #include + #include + + int main(void) + { + " + for syscall in $(cat ${TEMP}/syscall-names.txt); do + printf " + #ifdef __NR_$syscall + printf(\"$syscall %%d" + # i know the following print is ugly, but dash and bash + # treat double quoted strings in a different way and we + # really need to inject '\n' character in the C code + # rather than carriage return + printf '\\n' + printf "\", __NR_$syscall); + #endif + " + done + printf " return 0; + }" + ) >${TEMP}/list-syscalls.c +} + +export_headers() { + make -s -C ${KERNELSRC} ARCH=${arch} O=${LINUX_HEADERS} \ + headers_install >/dev/null 2>&1 +} + +do_all_tables() { + for archdir in ${KERNELSRC}/arch/*; do + arch=$(basename $archdir) + + bits=64 + extraflags= + + case ${arch} in + Kconfig) + continue + ;; + um) + continue + ;; + esac + + export_headers + grab_syscall_names_from_unistd_h + + case ${arch} in + arm) + bits=32 + arch=armoabi extraflags= generate_table + arch=arm extraflags=-D__ARM_EABI__ generate_table + ;; + loongarch) + # 32-bit variant of loongarch may appear + arch=loongarch64 extraflags=-D_LOONGARCH_SZLONG=64 generate_table + ;; + mips) + arch=mips64 extraflags=-D_MIPS_SIM=_MIPS_SIM_ABI64 generate_table + bits=32 + arch=mipso32 extraflags=-D_MIPS_SIM=_MIPS_SIM_ABI32 generate_table + arch=mips64n32 extraflags=-D_MIPS_SIM=_MIPS_SIM_NABI32 generate_table + ;; + powerpc) + generate_table + arch=powerpc64 generate_table + ;; + riscv) + arch=riscv64 extraflags=-D__LP64__ generate_table + bits=32 + arch=riscv32 extraflags=-D__SIZEOF_POINTER__=4 generate_table + ;; + s390) + bits=32 + generate_table + bits=64 + arch=s390x generate_table + ;; + sparc) + bits=32 + extraflags=-D__32bit_syscall_numbers__ generate_table + bits=64 + arch=sparc64 extraflags=-D__arch64__ generate_table + ;; + x86) + arch=x86_64 extraflags=-D__LP64__ generate_table + bits=32 + arch=i386 generate_table + arch=x32 extraflags=-D__ILP32__ generate_table + ;; + arc | csky | hexagon | m68k | microblaze | nios2 | openrisc | sh | xtensa) + bits=32 generate_table + ;; + *) + generate_table + ;; + esac + done +} + +copy_supported_arch() { + while IFS= read -r arch; do + if [ -f "${TEMP}/${arch}.in" ]; then + echo "- ${arch}" + cp "${TEMP}/${arch}.in" "${SCRIPT_DIR}/${arch}.in" + fi + done <${SUPPORTED_ARCH} +} + +echo "Temporary directory ${TEMP}" +echo "Extracting syscalls" + +grab_syscall_names_from_tables +generate_list_syscalls_c + +do_all_tables + +echo "Copying supported syscalls" +copy_supported_arch diff --git a/include/lapi/syscalls/generate_syscalls.sh b/include/lapi/syscalls/generate_syscalls.sh new file mode 100644 index 00000000..19f280df --- /dev/null +++ b/include/lapi/syscalls/generate_syscalls.sh @@ -0,0 +1,110 @@ +#!/bin/sh -eu +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Generate the syscalls.h file, merging all architectures syscalls input file +# which are in the current folder and defined inside supported-arch.txt file. + +SYSCALLS_FILE="$1" + +if [ -z "${SYSCALLS_FILE}" ]; then + echo "Please provide the syscalls.h directory:" + echo "" + echo "$0 path/of/syscalls.h" + echo "" + exit 1 +fi + +SCRIPT_DIR="$(realpath $(dirname "$0"))" +SUPPORTED_ARCH="${SCRIPT_DIR}/supported-arch.txt" + +echo '// SPDX-License-Identifier: GPL-2.0-or-later +/************************************************ + * GENERATED FILE: DO NOT EDIT/PATCH THIS FILE * + * change your arch specific .in file instead * + ************************************************/ + +/* + * Here we stick all the ugly *fallback* logic for linux + * system call numbers (those __NR_ thingies). + */ + +#ifndef LAPI_SYSCALLS_H__ +#define LAPI_SYSCALLS_H__ + +#include +#include +#include + +#ifdef TST_TEST_H__ +#define TST_SYSCALL_BRK__(NR, SNR) ({ \ +tst_brk(TCONF, \ + "syscall(%d) " SNR " not supported on your arch", NR); \ +}) +#else +inline static void dummy_cleanup(void) {} + +#define TST_SYSCALL_BRK__(NR, SNR) ({ \ +tst_brkm(TCONF, dummy_cleanup, \ + "syscall(%d) " SNR " not supported on your arch", NR); \ +}) +#endif + +#define tst_syscall(NR, ...) ({ \ +intptr_t tst_ret; \ +if (NR == __LTP__NR_INVALID_SYSCALL) { \ + errno = ENOSYS; \ + tst_ret = -1; \ +} else { \ + tst_ret = syscall(NR, ##__VA_ARGS__); \ +} \ +if (tst_ret == -1 && errno == ENOSYS) { \ + TST_SYSCALL_BRK__(NR, #NR); \ +} \ +tst_ret; \ +}) + +#define __LTP__NR_INVALID_SYSCALL -1' >${SYSCALLS_FILE} + +while IFS= read -r arch; do + ( + echo + case ${arch} in + sparc64) echo "#if defined(__sparc__) && defined(__arch64__)" ;; + sparc) echo "#if defined(__sparc__) && !defined(__arch64__)" ;; + s390) echo "#if defined(__s390__) && !defined(__s390x__)" ;; + mips64n32) echo "#if defined(__mips__) && defined(_ABIN32)" ;; + mips64) echo "#if defined(__mips__) && defined(_ABI64)" ;; + mipso32) echo "#if defined(__mips__) && defined(_ABIO32) && _MIPS_SZLONG == 32" ;; + parisc) echo "#ifdef __hppa__" ;; + loongarch64) echo "#ifdef __loongarch__" ;; + arm64) echo "#ifdef __aarch64__" ;; + powerpc) echo "#if defined(__powerpc__) && !defined(__powerpc64__)" ;; + *) echo "#ifdef __${arch}__" ;; + esac + + while read -r line; do + set -- ${line} + syscall_nr="__NR_$1" + shift + + echo "# ifndef ${syscall_nr}" + echo "# define ${syscall_nr} $*" + echo "# endif" + done <"${SCRIPT_DIR}/${arch}.in" + echo "#endif" + echo + ) >>${SYSCALLS_FILE} +done <${SUPPORTED_ARCH} + +( + echo + echo "/* Common stubs */" + for num in $(awk '{print $1}' "${SCRIPT_DIR}/"*.in | sort -u); do + syscall_nr="__NR_${num}" + + echo "# ifndef ${syscall_nr}" + echo "# define ${syscall_nr} __LTP__NR_INVALID_SYSCALL" + echo "# endif" + done + echo "#endif" +) >>${SYSCALLS_FILE} diff --git a/include/lapi/syscalls/hppa.in b/include/lapi/syscalls/hppa.in deleted file mode 100755 index 8ebdafaf..00000000 --- a/include/lapi/syscalls/hppa.in +++ /dev/null @@ -1,46 +0,0 @@ -_sysctl 149 -openat 275 -mkdirat (__NR_openat + 1) -mknodat (__NR_openat + 2) -fchownat (__NR_openat + 3) -futimesat (__NR_openat + 4) -newfstatat (__NR_openat + 5) -fstatat64 (__NR_openat + 5) -unlinkat (__NR_openat + 6) -renameat (__NR_openat + 7) -linkat (__NR_openat + 8) -symlinkat (__NR_openat + 9) -readlinkat (__NR_openat + 10) -fchmodat (__NR_openat + 11) -faccessat (__NR_openat + 12) -splice 291 -tee 293 -vmsplice 294 -syncfs 327 -setns 328 -process_vm_readv 330 -process_vm_writev 331 -memfd_create 340 -membarrier 343 -execveat 342 -mlock2 345 -copy_file_range 346 -preadv2 347 -pwritev2 348 -io_pgetevents 350 -pidfd_send_signal 424 -io_uring_setup 425 -io_uring_enter 426 -io_uring_register 427 -open_tree 428 -move_mount 429 -fsopen 430 -fsconfig 431 -fsmount 432 -fspick 433 -pidfd_open 434 -close_range 436 -faccessat2 439 -epoll_pwait2 441 -quotactl_fd 443 -futex_waitv 449 diff --git a/include/lapi/syscalls/i386.in b/include/lapi/syscalls/i386.in index 1472631c..38ea71fb 100755 --- a/include/lapi/syscalls/i386.in +++ b/include/lapi/syscalls/i386.in @@ -111,7 +111,6 @@ olduname 109 iopl 110 vhangup 111 idle 112 -vm86old 113 wait4 114 swapoff 115 sysinfo 116 @@ -132,7 +131,6 @@ get_kernel_syms 130 quotactl 131 getpgid 132 fchdir 133 -bdflush 134 sysfs 135 personality 136 afs_syscall 137 @@ -164,7 +162,6 @@ nanosleep 162 mremap 163 setresuid 164 getresuid 165 -vm86 166 query_module 167 poll 168 nfsservctl 169 @@ -218,7 +215,6 @@ setfsgid32 216 pivot_root 217 mincore 218 madvise 219 -madvise1 219 getdents64 220 fcntl64 221 gettid 224 @@ -428,6 +424,33 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +memfd_secret 447 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/ia64.in b/include/lapi/syscalls/ia64.in index 0ea6e972..cd770bac 100755 --- a/include/lapi/syscalls/ia64.in +++ b/include/lapi/syscalls/ia64.in @@ -343,4 +343,12 @@ pidfd_getfd 1462 faccessat2 1463 epoll_pwait2 1465 quotactl_fd 1467 +landlock_create_ruleset 1468 +landlock_add_rule 1469 +landlock_restrict_self 1470 futex_waitv 1473 +cachestat 1475 +fchmodat2 1476 +mseal 1486 +statmount 1481 +listmount 1482 diff --git a/include/lapi/syscalls/aarch64.in b/include/lapi/syscalls/loongarch64.in old mode 100755 new mode 100644 similarity index 89% rename from include/lapi/syscalls/aarch64.in rename to include/lapi/syscalls/loongarch64.in index 2cb6c2d8..5407b86e --- a/include/lapi/syscalls/aarch64.in +++ b/include/lapi/syscalls/loongarch64.in @@ -36,7 +36,6 @@ mkdirat 34 unlinkat 35 symlinkat 36 linkat 37 -renameat 38 umount2 39 mount 40 pivot_root 41 @@ -77,12 +76,11 @@ vmsplice 75 splice 76 tee 77 readlinkat 78 -fstatat 79 +newfstatat 79 fstat 80 sync 81 fsync 82 fdatasync 83 -sync_file_range2 84 sync_file_range 84 timerfd_create 85 timerfd_settime 86 @@ -162,8 +160,6 @@ setgroups 159 uname 160 sethostname 161 setdomainname 162 -getrlimit 163 -setrlimit 164 getrusage 165 umask 166 prctl 167 @@ -294,7 +290,32 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 futex_waitv 449 -_sysctl 1078 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/mips_n64.in b/include/lapi/syscalls/mips64.in old mode 100755 new mode 100644 similarity index 91% rename from include/lapi/syscalls/mips_n64.in rename to include/lapi/syscalls/mips64.in index 6e15f43b..436d5c68 --- a/include/lapi/syscalls/mips_n64.in +++ b/include/lapi/syscalls/mips64.in @@ -175,7 +175,6 @@ nfsservctl 5173 getpmsg 5174 putpmsg 5175 afs_syscall 5176 -reserved177 5177 gettid 5178 readahead 5179 setxattr 5180 @@ -191,7 +190,6 @@ removexattr 5189 lremovexattr 5190 fremovexattr 5191 tkill 5192 -reserved193 5193 futex 5194 sched_setaffinity 5195 sched_getaffinity 5196 @@ -346,4 +344,28 @@ process_madvise 5440 epoll_pwait2 5441 mount_setattr 5442 quotactl_fd 5443 +landlock_create_ruleset 5444 +landlock_add_rule 5445 +landlock_restrict_self 5446 +process_mrelease 5448 futex_waitv 5449 +set_mempolicy_home_node 5450 +cachestat 5451 +fchmodat2 5452 +map_shadow_stack 5453 +futex_wake 5454 +futex_wait 5455 +futex_requeue 5456 +statmount 5457 +listmount 5458 +lsm_get_self_attr 5459 +lsm_set_self_attr 5460 +lsm_list_modules 5461 +mseal 5462 +setxattrat 5463 +getxattrat 5464 +listxattrat 5465 +removexattrat 5466 +open_tree_attr 5467 +file_getattr 5468 +file_setattr 5469 diff --git a/include/lapi/syscalls/mips_n32.in b/include/lapi/syscalls/mips64n32.in old mode 100755 new mode 100644 similarity index 92% rename from include/lapi/syscalls/mips_n32.in rename to include/lapi/syscalls/mips64n32.in index e818c9d9..860a19c8 --- a/include/lapi/syscalls/mips_n32.in +++ b/include/lapi/syscalls/mips64n32.in @@ -175,7 +175,6 @@ nfsservctl 6173 getpmsg 6174 putpmsg 6175 afs_syscall 6176 -reserved177 6177 gettid 6178 readahead 6179 setxattr 6180 @@ -191,7 +190,6 @@ removexattr 6189 lremovexattr 6190 fremovexattr 6191 tkill 6192 -reserved193 6193 futex 6194 sched_setaffinity 6195 sched_getaffinity 6196 @@ -370,4 +368,28 @@ process_madvise 6440 epoll_pwait2 6441 mount_setattr 6442 quotactl_fd 6443 +landlock_create_ruleset 6444 +landlock_add_rule 6445 +landlock_restrict_self 6446 +process_mrelease 6448 futex_waitv 6449 +set_mempolicy_home_node 6450 +cachestat 6451 +fchmodat2 6452 +map_shadow_stack 6453 +futex_wake 6454 +futex_wait 6455 +futex_requeue 6456 +statmount 6457 +listmount 6458 +lsm_get_self_attr 6459 +lsm_set_self_attr 6460 +lsm_list_modules 6461 +mseal 6462 +setxattrat 6463 +getxattrat 6464 +listxattrat 6465 +removexattrat 6466 +open_tree_attr 6467 +file_getattr 6468 +file_setattr 6469 diff --git a/include/lapi/syscalls/mips_o32.in b/include/lapi/syscalls/mipso32.in old mode 100755 new mode 100644 similarity index 92% rename from include/lapi/syscalls/mips_o32.in rename to include/lapi/syscalls/mipso32.in index 921d5d33..5e53e46c --- a/include/lapi/syscalls/mips_o32.in +++ b/include/lapi/syscalls/mipso32.in @@ -16,7 +16,6 @@ mknod 4014 chmod 4015 lchown 4016 break 4017 -unused18 4018 lseek 4019 getpid 4020 mount 4021 @@ -26,7 +25,6 @@ getuid 4024 stime 4025 ptrace 4026 alarm 4027 -unused28 4028 pause 4029 utime 4030 stty 4031 @@ -80,9 +78,7 @@ gettimeofday 4078 settimeofday 4079 getgroups 4080 setgroups 4081 -reserved82 4082 symlink 4083 -unused84 4084 readlink 4085 uselib 4086 swapon 4087 @@ -111,7 +107,6 @@ unused109 4109 iopl 4110 vhangup 4111 idle 4112 -vm86 4113 wait4 4114 swapoff 4115 sysinfo 4116 @@ -132,7 +127,6 @@ get_kernel_syms 4130 quotactl 4131 getpgid 4132 fchdir 4133 -bdflush 4134 sysfs 4135 personality 4136 afs_syscall 4137 @@ -148,7 +142,6 @@ writev 4146 cacheflush 4147 cachectl 4148 sysmips 4149 -unused150 4150 getsid 4151 fdatasync 4152 _sysctl 4153 @@ -219,7 +212,6 @@ mincore 4217 madvise 4218 getdents64 4219 fcntl64 4220 -reserved221 4221 gettid 4222 readahead 4223 setxattr 4224 @@ -416,4 +408,28 @@ process_madvise 4440 epoll_pwait2 4441 mount_setattr 4442 quotactl_fd 4443 +landlock_create_ruleset 4444 +landlock_add_rule 4445 +landlock_restrict_self 4446 +process_mrelease 4448 futex_waitv 4449 +set_mempolicy_home_node 4450 +cachestat 4451 +fchmodat2 4452 +map_shadow_stack 4453 +futex_wake 4454 +futex_wait 4455 +futex_requeue 4456 +statmount 4457 +listmount 4458 +lsm_get_self_attr 4459 +lsm_set_self_attr 4460 +lsm_list_modules 4461 +mseal 4462 +setxattrat 4463 +getxattrat 4464 +listxattrat 4465 +removexattrat 4466 +open_tree_attr 4467 +file_getattr 4468 +file_setattr 4469 diff --git a/include/lapi/syscalls/parisc.in b/include/lapi/syscalls/parisc.in new file mode 100644 index 00000000..23875669 --- /dev/null +++ b/include/lapi/syscalls/parisc.in @@ -0,0 +1,404 @@ +restart_syscall 0 +exit 1 +fork 2 +read 3 +write 4 +open 5 +close 6 +waitpid 7 +creat 8 +link 9 +unlink 10 +execve 11 +chdir 12 +time 13 +mknod 14 +chmod 15 +lchown 16 +socket 17 +stat 18 +lseek 19 +getpid 20 +mount 21 +bind 22 +setuid 23 +getuid 24 +stime 25 +ptrace 26 +alarm 27 +fstat 28 +pause 29 +utime 30 +connect 31 +listen 32 +access 33 +nice 34 +accept 35 +sync 36 +kill 37 +rename 38 +mkdir 39 +rmdir 40 +dup 41 +pipe 42 +times 43 +getsockname 44 +brk 45 +setgid 46 +getgid 47 +signal 48 +geteuid 49 +getegid 50 +acct 51 +umount2 52 +getpeername 53 +ioctl 54 +fcntl 55 +socketpair 56 +setpgid 57 +send 58 +uname 59 +umask 60 +chroot 61 +ustat 62 +dup2 63 +getppid 64 +getpgrp 65 +setsid 66 +pivot_root 67 +sgetmask 68 +ssetmask 69 +setreuid 70 +setregid 71 +mincore 72 +sigpending 73 +sethostname 74 +setrlimit 75 +getrlimit 76 +getrusage 77 +gettimeofday 78 +settimeofday 79 +getgroups 80 +setgroups 81 +sendto 82 +symlink 83 +lstat 84 +readlink 85 +uselib 86 +swapon 87 +reboot 88 +mmap2 89 +mmap 90 +munmap 91 +truncate 92 +ftruncate 93 +fchmod 94 +fchown 95 +getpriority 96 +setpriority 97 +recv 98 +statfs 99 +fstatfs 100 +stat64 101 +syslog 103 +setitimer 104 +getitimer 105 +capget 106 +capset 107 +pread64 108 +pwrite64 109 +getcwd 110 +vhangup 111 +fstat64 112 +vfork 113 +wait4 114 +swapoff 115 +sysinfo 116 +shutdown 117 +fsync 118 +madvise 119 +clone 120 +setdomainname 121 +sendfile 122 +recvfrom 123 +adjtimex 124 +mprotect 125 +sigprocmask 126 +init_module 128 +delete_module 129 +quotactl 131 +getpgid 132 +fchdir 133 +sysfs 135 +personality 136 +setfsuid 138 +setfsgid 139 +_llseek 140 +getdents 141 +_newselect 142 +flock 143 +msync 144 +readv 145 +writev 146 +getsid 147 +fdatasync 148 +_sysctl 149 +mlock 150 +munlock 151 +mlockall 152 +munlockall 153 +sched_setparam 154 +sched_getparam 155 +sched_setscheduler 156 +sched_getscheduler 157 +sched_yield 158 +sched_get_priority_max 159 +sched_get_priority_min 160 +sched_rr_get_interval 161 +nanosleep 162 +mremap 163 +setresuid 164 +getresuid 165 +sigaltstack 166 +poll 168 +setresgid 170 +getresgid 171 +prctl 172 +rt_sigreturn 173 +rt_sigaction 174 +rt_sigprocmask 175 +rt_sigpending 176 +rt_sigtimedwait 177 +rt_sigqueueinfo 178 +rt_sigsuspend 179 +chown 180 +setsockopt 181 +getsockopt 182 +sendmsg 183 +recvmsg 184 +semop 185 +semget 186 +semctl 187 +msgsnd 188 +msgrcv 189 +msgget 190 +msgctl 191 +shmat 192 +shmdt 193 +shmget 194 +shmctl 195 +lstat64 198 +truncate64 199 +ftruncate64 200 +getdents64 201 +fcntl64 202 +gettid 206 +readahead 207 +tkill 208 +sendfile64 209 +futex 210 +sched_setaffinity 211 +sched_getaffinity 212 +io_setup 215 +io_destroy 216 +io_getevents 217 +io_submit 218 +io_cancel 219 +exit_group 222 +lookup_dcookie 223 +epoll_create 224 +epoll_ctl 225 +epoll_wait 226 +remap_file_pages 227 +semtimedop 228 +mq_open 229 +mq_unlink 230 +mq_timedsend 231 +mq_timedreceive 232 +mq_notify 233 +mq_getsetattr 234 +waitid 235 +fadvise64_64 236 +set_tid_address 237 +setxattr 238 +lsetxattr 239 +fsetxattr 240 +getxattr 241 +lgetxattr 242 +fgetxattr 243 +listxattr 244 +llistxattr 245 +flistxattr 246 +removexattr 247 +lremovexattr 248 +fremovexattr 249 +timer_create 250 +timer_settime 251 +timer_gettime 252 +timer_getoverrun 253 +timer_delete 254 +clock_settime 255 +clock_gettime 256 +clock_getres 257 +clock_nanosleep 258 +tgkill 259 +mbind 260 +get_mempolicy 261 +set_mempolicy 262 +add_key 264 +request_key 265 +keyctl 266 +ioprio_set 267 +ioprio_get 268 +inotify_init 269 +inotify_add_watch 270 +inotify_rm_watch 271 +migrate_pages 272 +pselect6 273 +ppoll 274 +openat 275 +mkdirat 276 +mknodat 277 +fchownat 278 +futimesat 279 +fstatat64 280 +unlinkat 281 +renameat 282 +linkat 283 +symlinkat 284 +readlinkat 285 +fchmodat 286 +faccessat 287 +unshare 288 +set_robust_list 289 +get_robust_list 290 +splice 291 +sync_file_range 292 +tee 293 +vmsplice 294 +move_pages 295 +getcpu 296 +epoll_pwait 297 +statfs64 298 +fstatfs64 299 +kexec_load 300 +utimensat 301 +signalfd 302 +eventfd 304 +fallocate 305 +timerfd_create 306 +timerfd_settime 307 +timerfd_gettime 308 +signalfd4 309 +eventfd2 310 +epoll_create1 311 +dup3 312 +pipe2 313 +inotify_init1 314 +preadv 315 +pwritev 316 +rt_tgsigqueueinfo 317 +perf_event_open 318 +recvmmsg 319 +accept4 320 +prlimit64 321 +fanotify_init 322 +fanotify_mark 323 +clock_adjtime 324 +name_to_handle_at 325 +open_by_handle_at 326 +syncfs 327 +setns 328 +sendmmsg 329 +process_vm_readv 330 +process_vm_writev 331 +kcmp 332 +finit_module 333 +sched_setattr 334 +sched_getattr 335 +utimes 336 +renameat2 337 +seccomp 338 +getrandom 339 +memfd_create 340 +bpf 341 +execveat 342 +membarrier 343 +userfaultfd 344 +mlock2 345 +copy_file_range 346 +preadv2 347 +pwritev2 348 +statx 349 +io_pgetevents 350 +pkey_mprotect 351 +pkey_alloc 352 +pkey_free 353 +rseq 354 +kexec_file_load 355 +cacheflush 356 +clock_gettime64 403 +clock_settime64 404 +clock_adjtime64 405 +clock_getres_time64 406 +clock_nanosleep_time64 407 +timer_gettime64 408 +timer_settime64 409 +timerfd_gettime64 410 +timerfd_settime64 411 +utimensat_time64 412 +pselect6_time64 413 +ppoll_time64 414 +io_pgetevents_time64 416 +recvmmsg_time64 417 +mq_timedsend_time64 418 +mq_timedreceive_time64 419 +semtimedop_time64 420 +rt_sigtimedwait_time64 421 +futex_time64 422 +sched_rr_get_interval_time64 423 +pidfd_send_signal 424 +io_uring_setup 425 +io_uring_enter 426 +io_uring_register 427 +open_tree 428 +move_mount 429 +fsopen 430 +fsconfig 431 +fsmount 432 +fspick 433 +pidfd_open 434 +clone3 435 +close_range 436 +openat2 437 +pidfd_getfd 438 +faccessat2 439 +process_madvise 440 +epoll_pwait2 441 +mount_setattr 442 +quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 +futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/powerpc.in b/include/lapi/syscalls/powerpc.in index 545d9d3d..6911f9af 100755 --- a/include/lapi/syscalls/powerpc.in +++ b/include/lapi/syscalls/powerpc.in @@ -111,7 +111,6 @@ olduname 109 iopl 110 vhangup 111 idle 112 -vm86 113 wait4 114 swapoff 115 sysinfo 116 @@ -132,7 +131,6 @@ get_kernel_syms 130 quotactl 131 getpgid 132 fchdir 133 -bdflush 134 sysfs 135 personality 136 afs_syscall 137 @@ -199,7 +197,6 @@ fstat64 197 pciconfig_read 198 pciconfig_write 199 pciconfig_iobase 200 -multiplexer 201 getdents64 202 pivot_root 203 fcntl64 204 @@ -253,7 +250,6 @@ statfs64 252 fstatfs64 253 fadvise64_64 254 rtas 255 -sys_debug_setcontext 256 migrate_pages 258 mbind 259 get_mempolicy 260 @@ -287,7 +283,6 @@ mkdirat 287 mknodat 288 fchownat 289 futimesat 290 -newfstatat 291 fstatat64 291 unlinkat 292 renameat 293 @@ -374,7 +369,6 @@ pkey_free 385 pkey_mprotect 386 rseq 387 io_pgetevents 388 -semtimedop 392 semget 393 semctl 394 shmget 395 @@ -421,6 +415,32 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/powerpc64.in b/include/lapi/syscalls/powerpc64.in index 545d9d3d..98190ba2 100755 --- a/include/lapi/syscalls/powerpc64.in +++ b/include/lapi/syscalls/powerpc64.in @@ -111,7 +111,6 @@ olduname 109 iopl 110 vhangup 111 idle 112 -vm86 113 wait4 114 swapoff 115 sysinfo 116 @@ -132,7 +131,6 @@ get_kernel_syms 130 quotactl 131 getpgid 132 fchdir 133 -bdflush 134 sysfs 135 personality 136 afs_syscall 137 @@ -190,19 +188,11 @@ putpmsg 188 vfork 189 ugetrlimit 190 readahead 191 -mmap2 192 -truncate64 193 -ftruncate64 194 -stat64 195 -lstat64 196 -fstat64 197 pciconfig_read 198 pciconfig_write 199 pciconfig_iobase 200 -multiplexer 201 getdents64 202 pivot_root 203 -fcntl64 204 madvise 205 mincore 206 gettid 207 @@ -223,7 +213,6 @@ futex 221 sched_setaffinity 222 sched_getaffinity 223 tuxcall 225 -sendfile64 226 io_setup 227 io_destroy 228 io_getevents 229 @@ -251,9 +240,7 @@ tgkill 250 utimes 251 statfs64 252 fstatfs64 253 -fadvise64_64 254 rtas 255 -sys_debug_setcontext 256 migrate_pages 258 mbind 259 get_mempolicy 260 @@ -288,7 +275,6 @@ mknodat 288 fchownat 289 futimesat 290 newfstatat 291 -fstatat64 291 unlinkat 292 renameat 293 linkat 294 @@ -385,26 +371,6 @@ msgget 399 msgsnd 400 msgrcv 401 msgctl 402 -clock_gettime64 403 -clock_settime64 404 -clock_adjtime64 405 -clock_getres_time64 406 -clock_nanosleep_time64 407 -timer_gettime64 408 -timer_settime64 409 -timerfd_gettime64 410 -timerfd_settime64 411 -utimensat_time64 412 -pselect6_time64 413 -ppoll_time64 414 -io_pgetevents_time64 416 -recvmmsg_time64 417 -mq_timedsend_time64 418 -mq_timedreceive_time64 419 -semtimedop_time64 420 -rt_sigtimedwait_time64 421 -futex_time64 422 -sched_rr_get_interval_time64 423 pidfd_send_signal 424 io_uring_setup 425 io_uring_enter 426 @@ -421,6 +387,32 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/regen.sh b/include/lapi/syscalls/regen.sh deleted file mode 100755 index 97027e2f..00000000 --- a/include/lapi/syscalls/regen.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/sh - -output="syscalls.h" -rm -f "${output}".[1-9]* -output_pid="${output}.$$" - -max_jobs=$(getconf _NPROCESSORS_ONLN 2>/dev/null) -: ${max_jobs:=1} - -srcdir=${0%/*} - -err() { - echo "$*" 1>&2 - exit 1 -} - -cat << EOF > "${output_pid}" -/************************************************ - * GENERATED FILE: DO NOT EDIT/PATCH THIS FILE * - * change your arch specific .in file instead * - ************************************************/ - -/* - * Here we stick all the ugly *fallback* logic for linux - * system call numbers (those __NR_ thingies). - * - * Licensed under the GPLv2 or later, see the COPYING file. - */ - -#ifndef LAPI_SYSCALLS_H__ -#define LAPI_SYSCALLS_H__ - -#include -#include -#include -#include "cleanup.c" - -#ifdef TST_TEST_H__ -#define TST_SYSCALL_BRK__(NR, SNR) ({ \\ - tst_brk(TCONF, \\ - "syscall(%d) " SNR " not supported on your arch", NR); \\ -}) -#else -#define TST_SYSCALL_BRK__(NR, SNR) ({ \\ - tst_brkm(TCONF, CLEANUP, \\ - "syscall(%d) " SNR " not supported on your arch", NR); \\ -}) -#endif - -#define tst_syscall(NR, ...) ({ \\ - intptr_t tst_ret; \\ - if (NR == __LTP__NR_INVALID_SYSCALL) { \\ - errno = ENOSYS; \\ - tst_ret = -1; \\ - } else { \\ - tst_ret = syscall(NR, ##__VA_ARGS__); \\ - } \\ - if (tst_ret == -1 && errno == ENOSYS) { \\ - TST_SYSCALL_BRK__(NR, #NR); \\ - } \\ - tst_ret; \\ -}) - -EOF - -jobs=0 -for arch in $(cat "${srcdir}/order") ; do - ( - echo "Generating data for arch $arch ... " - - ( - echo - case ${arch} in - sparc64) echo "#if defined(__sparc__) && defined(__arch64__)" ;; - sparc) echo "#if defined(__sparc__) && !defined(__arch64__)" ;; - s390) echo "#if defined(__s390__) && !defined(__s390x__)" ;; - mips_n32) echo "#if defined(__mips__) && defined(_ABIN32)" ;; - mips_n64) echo "#if defined(__mips__) && defined(_ABI64)" ;; - mips_o32) echo "#if defined(__mips__) && defined(_ABIO32) && _MIPS_SZLONG == 32" ;; - *) echo "#ifdef __${arch}__" ;; - esac - while read line ; do - set -- ${line} - nr="__NR_$1" - shift - if [ $# -eq 0 ] ; then - err "invalid line found: $line" - fi - echo "# ifndef ${nr}" - echo "# define ${nr} $*" - echo "# endif" - done < "${srcdir}/${arch}.in" - echo "#endif" - echo - ) >> "${output_pid}.${arch}" - - ) & - - jobs=$(( jobs + 1 )) - if [ ${jobs} -ge ${max_jobs} ] ; then - wait || exit 1 - jobs=0 - fi -done - -echo "Generating stub list ... " -( -echo -echo "/* Common stubs */" -echo "#define __LTP__NR_INVALID_SYSCALL -1" >> "${output_pid}" -for nr in $(awk '{print $1}' "${srcdir}/"*.in | sort -u) ; do - nr="__NR_${nr}" - echo "# ifndef ${nr}" - echo "# define ${nr} __LTP__NR_INVALID_SYSCALL" - echo "# endif" -done -echo "#endif" -) >> "${output_pid}._footer" - -wait || exit 1 - -printf "Combining them all ... " -for arch in $(cat "${srcdir}/order") _footer ; do - cat "${output_pid}.${arch}" -done >> "${output_pid}" -mv "${output_pid}" "../${output}" -rm -f "${output_pid}"* -echo "OK!" diff --git a/include/lapi/syscalls/s390.in b/include/lapi/syscalls/s390.in index 7213ac5f..a6cb85da 100755 --- a/include/lapi/syscalls/s390.in +++ b/include/lapi/syscalls/s390.in @@ -112,7 +112,6 @@ get_kernel_syms 130 quotactl 131 getpgid 132 fchdir 133 -bdflush 134 sysfs 135 personality 136 afs_syscall 137 @@ -408,6 +407,33 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +memfd_secret 447 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/s390x.in b/include/lapi/syscalls/s390x.in index 879012e2..31f3ec55 100755 --- a/include/lapi/syscalls/s390x.in +++ b/include/lapi/syscalls/s390x.in @@ -96,7 +96,6 @@ get_kernel_syms 130 quotactl 131 getpgid 132 fchdir 133 -bdflush 134 sysfs 135 personality 136 afs_syscall 137 @@ -356,6 +355,33 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +memfd_secret 447 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/sh.in b/include/lapi/syscalls/sh.in index 7d5192a2..ac281acf 100755 --- a/include/lapi/syscalls/sh.in +++ b/include/lapi/syscalls/sh.in @@ -115,7 +115,6 @@ delete_module 129 quotactl 131 getpgid 132 fchdir 133 -bdflush 134 sysfs 135 personality 136 setfsuid 138 @@ -357,6 +356,7 @@ pkey_mprotect 384 pkey_alloc 385 pkey_free 386 rseq 387 +sync_file_range2 388 semget 393 semctl 394 shmget 395 @@ -402,6 +402,32 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/sparc.in b/include/lapi/syscalls/sparc.in index 91d2fb1c..ffc0d9f7 100755 --- a/include/lapi/syscalls/sparc.in +++ b/include/lapi/syscalls/sparc.in @@ -221,7 +221,6 @@ create_module 221 delete_module 222 get_kernel_syms 223 getpgid 224 -bdflush 225 sysfs 226 afs_syscall 227 setfsuid 228 @@ -407,6 +406,32 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/sparc64.in b/include/lapi/syscalls/sparc64.in index 1f2fc59b..992bd307 100755 --- a/include/lapi/syscalls/sparc64.in +++ b/include/lapi/syscalls/sparc64.in @@ -29,8 +29,6 @@ alarm 27 sigaltstack 28 pause 29 utime 30 -lchown32 31 -fchown32 32 access 33 nice 34 sync 36 @@ -206,7 +204,6 @@ create_module 221 delete_module 222 get_kernel_syms 223 getpgid 224 -bdflush 225 sysfs 226 afs_syscall 227 setfsuid 228 @@ -372,6 +369,32 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +process_mrelease 448 futex_waitv 449 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/syscalls/strip_syscall.awk b/include/lapi/syscalls/strip_syscall.awk deleted file mode 100755 index e8dff422..00000000 --- a/include/lapi/syscalls/strip_syscall.awk +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/awk -f -# -# Dumb script that can be used to strip all of the syscall information from -# the arch-respective unistd*.h. -# -# Examples: -# -# 1. Grab the i386 32-bit syscalls from unistd_32.h and put them in i386.in -# strip_syscall.awk arch/x86/include/asm/unistd_32.h > i386.in -# - -/^#define[[:space:]]+__NR_[0-9a-z]+/ { - - sub (/#define[[:space:]]+__NR_/, "", $0); - sub (/[[:space:]]*(\/\*.*)/, "", $0); - sub (/[[:space:]]+/, " ", $0); - - print -} diff --git a/include/lapi/syscalls/order b/include/lapi/syscalls/supported-arch.txt old mode 100755 new mode 100644 similarity index 58% rename from include/lapi/syscalls/order rename to include/lapi/syscalls/supported-arch.txt index c18aa38c..c5c5191a --- a/include/lapi/syscalls/order +++ b/include/lapi/syscalls/supported-arch.txt @@ -1,13 +1,13 @@ -aarch64 arc +arm64 arm -hppa i386 ia64 -loongarch -mips_n32 -mips_n64 -mips_o32 +loongarch64 +mips64n32 +mips64 +mipso32 +parisc powerpc64 powerpc s390x diff --git a/include/lapi/syscalls/x86_64.in b/include/lapi/syscalls/x86_64.in index dc61aa56..e9c0d059 100755 --- a/include/lapi/syscalls/x86_64.in +++ b/include/lapi/syscalls/x86_64.in @@ -333,6 +333,7 @@ pkey_free 331 statx 332 io_pgetevents 333 rseq 334 +uretprobe 335 pidfd_send_signal 424 io_uring_setup 425 io_uring_enter 426 @@ -349,42 +350,33 @@ close_range 436 openat2 437 pidfd_getfd 438 faccessat2 439 +process_madvise 440 epoll_pwait2 441 +mount_setattr 442 quotactl_fd 443 +landlock_create_ruleset 444 +landlock_add_rule 445 +landlock_restrict_self 446 +memfd_secret 447 +process_mrelease 448 futex_waitv 449 -rt_sigaction 512 -rt_sigreturn 513 -ioctl 514 -readv 515 -writev 516 -recvfrom 517 -sendmsg 518 -recvmsg 519 -execve 520 -ptrace 521 -rt_sigpending 522 -rt_sigtimedwait 523 -rt_sigqueueinfo 524 -sigaltstack 525 -timer_create 526 -mq_notify 527 -kexec_load 528 -waitid 529 -set_robust_list 530 -get_robust_list 531 -vmsplice 532 -move_pages 533 -preadv 534 -pwritev 535 -rt_tgsigqueueinfo 536 -recvmmsg 537 -sendmmsg 538 -process_vm_readv 539 -process_vm_writev 540 -setsockopt 541 -getsockopt 542 -io_setup 543 -io_submit 544 -execveat 545 -preadv2 546 -pwritev2 547 +set_mempolicy_home_node 450 +cachestat 451 +fchmodat2 452 +map_shadow_stack 453 +futex_wake 454 +futex_wait 455 +futex_requeue 456 +statmount 457 +listmount 458 +lsm_get_self_attr 459 +lsm_set_self_attr 460 +lsm_list_modules 461 +mseal 462 +setxattrat 463 +getxattrat 464 +listxattrat 465 +removexattrat 466 +open_tree_attr 467 +file_getattr 468 +file_setattr 469 diff --git a/include/lapi/tcp.h b/include/lapi/tcp.h index bb98f28f..87c5636f 100755 --- a/include/lapi/tcp.h +++ b/include/lapi/tcp.h @@ -12,6 +12,10 @@ # define TCP_FASTOPEN 23 #endif +#ifndef TCP_ULP +# define TCP_ULP 31 +#endif + #ifndef TCP_FASTOPEN_CONNECT # define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect */ #endif diff --git a/include/lapi/uinput.h b/include/lapi/uinput.h new file mode 100644 index 00000000..bdd6f466 --- /dev/null +++ b/include/lapi/uinput.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Petr Vorel + */ + +#ifndef LAPI_UINPUT_H__ +#define LAPI_UINPUT_H__ + +#include + +#ifndef UI_GET_SYSNAME +# define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 44, len) +#endif + +#endif /* LAPI_UINPUT_H__ */ diff --git a/include/lapi/preadv2.h b/include/lapi/uio.h old mode 100755 new mode 100644 similarity index 50% rename from include/lapi/preadv2.h rename to include/lapi/uio.h index db89547e..0ad2faac --- a/include/lapi/preadv2.h +++ b/include/lapi/uio.h @@ -7,6 +7,7 @@ #ifndef LAPI_PREADV2_H__ #define LAPI_PREADV2_H__ +#include #include "config.h" #include "lapi/syscalls.h" @@ -14,11 +15,27 @@ # define RWF_NOWAIT 0x00000008 #endif -#if !defined(HAVE_PREADV2) /* LO_HI_LONG taken from glibc */ # define LO_HI_LONG(val) (long) (val), (long) (((uint64_t) (val)) >> 32) +#if !defined(HAVE_PREADV) +static inline ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, + off_t offset) +{ + return tst_syscall(__NR_preadv, fd, iov, iovcnt, LO_HI_LONG(offset)); +} +#endif + +#if !defined(HAVE_PWRITEV) +static inline ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, + off_t offset) +{ + return tst_syscall(__NR_pwritev, fd, iov, iovcnt, LO_HI_LONG(offset)); +} +#endif + +#if !defined(HAVE_PREADV2) static inline ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags) { @@ -27,4 +44,15 @@ static inline ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt, } #endif +#if !defined(HAVE_PWRITEV2) +static inline ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt, + off_t offset, int flags) +{ + return tst_syscall(__NR_pwritev2, fd, iov, iovcnt, + LO_HI_LONG(offset), flags); +} +#endif + +#undef LO_HI_LONG + #endif /* LAPI_PREADV2_H__ */ diff --git a/include/lapi/vm_sockets.h b/include/lapi/vm_sockets.h index 07884e53..fda8c315 100755 --- a/include/lapi/vm_sockets.h +++ b/include/lapi/vm_sockets.h @@ -7,6 +7,7 @@ #define LAPI_VM_SOCKETS_H__ #include +#include "config.h" #if HAVE_LINUX_VM_SOCKETS_H # include @@ -16,4 +17,20 @@ # define VMADDR_CID_LOCAL 1 #endif +#ifndef HAVE_STRUCT_SOCKADDR_VM +struct sockaddr_vm { + unsigned short svm_family; + unsigned short svm_reserved1; + unsigned int svm_port; + unsigned int svm_cid; + unsigned char svm_flags; + unsigned char svm_zero[sizeof(struct sockaddr) - + sizeof(sa_family_t) - + sizeof(unsigned short) - + sizeof(unsigned int) - + sizeof(unsigned int) - + sizeof(unsigned char)]; +}; +#endif + #endif /* LAPI_VM_SOCKETS_H__ */ diff --git a/include/libswap.h b/include/libswap.h index d4b5301a..6904e8f4 100755 --- a/include/libswap.h +++ b/include/libswap.h @@ -1,24 +1,126 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * Copyright (c) Linux Test Project, 2021-2024 * Author: Stanislav Kholmanskikh */ -/* - * Contains common content for all swapon/swapoff tests +/** + * DOC: libltpswap + * + * Contains common content for all swapon/swapoff tests. */ #ifndef __LIBSWAP_H__ #define __LIBSWAP_H__ -/* - * Make a swap file - */ -int make_swapfile(const char *swapfile, int safe); +enum swapfile_method { + SWAPFILE_BY_SIZE, + SWAPFILE_BY_BLKS +}; /* + * Create a swapfile of a specified size or number of blocks. + */ +int make_swapfile(const char *file, const int lineno, + const char *swapfile, unsigned int num, + int safe, enum swapfile_method method); + +/** 65536 bytes is minimum for 64kb page size, let's use 1 MB */ +#define MINIMAL_SWAP_SIZE_MB 1 + +/** + * MAKE_SMALL_SWAPFILE - create small swap file. + * + * Macro to create small swap file. Size defined with MINIMAL_SWAP_SIZE_MB. + * + * @swapfile: swap filename. + */ +#define MAKE_SMALL_SWAPFILE(swapfile) \ + make_swapfile(__FILE__, __LINE__, swapfile, MINIMAL_SWAP_SIZE_MB, 0, \ + SWAPFILE_BY_SIZE) + +/** + * SAFE_MAKE_SMALL_SWAPFILE - create small swap file (safe version). + * + * Macro to create small swap file. Size defined with MINIMAL_SWAP_SIZE_MB. + * Includes safety checks to handle potential errors. + * + * @swapfile: swap filename. + */ +#define SAFE_MAKE_SMALL_SWAPFILE(swapfile) \ + make_swapfile(__FILE__, __LINE__, swapfile, MINIMAL_SWAP_SIZE_MB, 1, \ + SWAPFILE_BY_SIZE) + +/** + * MAKE_SWAPFILE_SIZE - create swap file (MB). + * + * Macro to create swap file, size specified in megabytes (MB). + * + * @swapfile: swap filename. + * @size: swap size in MB. + */ +#define MAKE_SWAPFILE_SIZE(swapfile, size) \ + make_swapfile(__FILE__, __LINE__, swapfile, size, 0, SWAPFILE_BY_SIZE) + +/** + * MAKE_SWAPFILE_BLKS - create swap file (blocks). + * + * Macro to create swap file, size specified in block numbers. + * + * @swapfile: swap filename. + * @blocks: number of blocks. + */ +#define MAKE_SWAPFILE_BLKS(swapfile, blocks) \ + make_swapfile(__FILE__, __LINE__, swapfile, blocks, 0, SWAPFILE_BY_BLKS) + +/** + * SAFE_MAKE_SWAPFILE_SIZE - create swap file (MB, safe version). + * + * Macro to safely create swap file, size specified in megabytes (MB). + * Includes safety checks to handle potential errors. + * + * @swapfile: swap file name. + * @size: swap size in MB. + */ +#define SAFE_MAKE_SWAPFILE_SIZE(swapfile, size) \ + make_swapfile(__FILE__, __LINE__, swapfile, size, 1, SWAPFILE_BY_SIZE) + +/** + * SAFE_MAKE_SWAPFILE_BLKS - create swap file (block, safe version) + * + * Macro to safely create swap file, size specified in block numbers. + * Includes safety checks to handle potential errors. + * + * @swapfile: swap file name. + * @blocks: number of blocks. + */ +#define SAFE_MAKE_SWAPFILE_BLKS(swapfile, blocks) \ + make_swapfile(__FILE__, __LINE__, swapfile, blocks, 1, SWAPFILE_BY_BLKS) + +/** + * is_swap_supported() - Check swapon/swapoff support. + * * Check swapon/swapoff support status of filesystems or files * we are testing on. + * + * @filename: swap file name. + * Return: true if swap is supported, false if not. */ -void is_swap_supported(const char *filename); +bool is_swap_supported(const char *filename); + +/** + * tst_max_swapfiles() - Get kernel constant MAX_SWAPFILES value. + * + * Return: MAX_SWAPFILES value. + */ +int tst_max_swapfiles(void); + +/** + * tst_count_swaps() - Get the used swapfiles number. + * + * Return: used swapfiles number. + */ +int tst_count_swaps(void); + #endif /* __LIBSWAP_H__ */ diff --git a/include/mk/automake.mk b/include/mk/automake.mk index 3ecdd314..a58f4042 100755 --- a/include/mk/automake.mk +++ b/include/mk/automake.mk @@ -1,24 +1,8 @@ -# -# Autotools include Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Autotools include Makefile. +# Copyright (c) Linux Test Project, 2010-2024 +# Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 -# # Override these variables to use non-system available tools. ACLOCAL ?= aclocal @@ -27,15 +11,12 @@ AUTOHEADER ?= autoheader AUTOMAKE ?= automake AUTOCONFED_SUBDIRS = \ - testcases/realtime \ testcases/open_posix_testsuite +CLEAN_SUBDIRS = $(AUTOCONFED_SUBDIRS) include + # We want to run this every single time to ensure that all of the prereq files # are there. -.PHONY: testcases/realtime/configure -testcases/realtime/configure: - $(MAKE) -C $(@D) autotools - .PHONY: testcases/open_posix_testsuite/configure testcases/open_posix_testsuite/configure: $(MAKE) -C $(@D) autotools @@ -66,7 +47,7 @@ m4/ltp-version.m4: VERSION sed -n '1{s:LTP-:m4_define([LTP_VERSION],[:;s:$$:]):;p;q}' $< > $@ .PHONY: automake -AUTOMAKE_FILES := config.guess config.sub install-sh missing stamp-h1 +AUTOMAKE_FILES := compile config.guess config.sub install-sh missing automake: aclocal $(AUTOMAKE_FILES) $(AUTOMAKE_FILES): m4/Makefile.in m4/Makefile.in: m4/Makefile.am aclocal.m4 @@ -76,19 +57,17 @@ m4/Makefile.in: m4/Makefile.am aclocal.m4 ac-clean:: $(RM) -rf autom4te.cache $(RM) -f config.log config.status - $(RM) -f include/config.h include/stamp-h1 $(RM) -f m4/Makefile m4/ltp-version.m4 - for d in $(AUTOCONFED_SUBDIRS); do \ + for d in $(CLEAN_SUBDIRS); do \ $(MAKE) -C "$(top_srcdir)/$$d" $@; \ done ac-distclean:: ac-clean ac-maintainer-clean:: ac-distclean - for d in $(AUTOCONFED_SUBDIRS); do \ + for d in $(CLEAN_SUBDIRS); do \ $(MAKE) -C "$(top_srcdir)/$$d" $@; \ done $(RM) -f aclocal.m4 configure $(AUTOMAKE_FILES) m4/Makefile.in - $(RM) -f include/*config.h.in # Don't include config.h, or make will (rightfully) whine about overriding # rules. @@ -97,12 +76,13 @@ ac-maintainer-clean:: ac-distclean # AUTOGENERATED_FILES = \ include/mk/config.mk \ + include/mk/config-openposix.mk \ include/mk/features.mk \ lib/ltp.pc \ m4/Makefile distclean:: %: clean ac-distclean - for d in $(AUTOCONFED_SUBDIRS); do \ + for d in $(CLEAN_SUBDIRS); do \ $(MAKE) -C "$(top_srcdir)/$$d" $@; \ done $(RM) -f $(AUTOGENERATED_FILES) diff --git a/include/mk/config-openposix.mk.in b/include/mk/config-openposix.mk.in index 9a91dcb7..54422aec 100755 --- a/include/mk/config-openposix.mk.in +++ b/include/mk/config-openposix.mk.in @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2016 # Parameters from the top level configure CC= @CC@ CFLAGS+= @CFLAGS@ diff --git a/include/mk/config.mk.in b/include/mk/config.mk.in index 22301e12..4c3da304 100755 --- a/include/mk/config.mk.in +++ b/include/mk/config.mk.in @@ -1,24 +1,7 @@ -# -# config.mk.in. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2009-2024 +# Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 -# # See this page for more info about LEX*: # http://www.gnu.org/software/hello/manual/autoconf/Particular-Programs.html @@ -28,6 +11,7 @@ AR := @AR@ CC := @CC@ LEX := @LEX@ RANLIB := @RANLIB@ +OBJCOPY := @OBJCOPY@ STRIP := @STRIP@ YACC := @YACC@ @@ -75,7 +59,7 @@ LDFLAGS := @LDFLAGS@ DEBUG_CFLAGS ?= -g -# for -fstrict-aliasing see doc/build-system-guide.txt +# for -fstrict-aliasing see doc/developers/build_system.rst. OPT_CFLAGS ?= -O2 -fno-strict-aliasing -pipe WCFLAGS ?= -Wall -W @GCC_WARN_OLDSTYLE@ @@ -86,6 +70,7 @@ LDFLAGS += $(WLDFLAGS) CFLAGS += $(DEBUG_CFLAGS) $(OPT_CFLAGS) $(WCFLAGS) $(STDCFLAGS) LTP_CFLAGS_NOPIE := @LTP_CFLAGS_NOPIE@ +LTP_CFLAGS_FFIXED_EBP := @LTP_CFLAGS_FFIXED_EBP@ ifeq ($(strip $(HOST_CFLAGS)),) HOST_CFLAGS := $(CFLAGS) diff --git a/include/mk/env_post.mk b/include/mk/env_post.mk index a00f31b0..ab31da73 100755 --- a/include/mk/env_post.mk +++ b/include/mk/env_post.mk @@ -1,25 +1,8 @@ -# -# Environment post-setup Makefile. -# -# Copyright (c) Linux Test Project, 2009-2020 -# Copyright (c) Cisco Systems Inc., 2009 -# -# 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. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Environment post-setup Makefile. +# Copyright (c) Linux Test Project, 2009-2025 +# Copyright (c) Cisco Systems Inc., 2009 # Ngie Cooper, July 2009 -# ENV_PRE_LOADED ?= $(error You must load env_pre.mk before including this file) @@ -39,10 +22,6 @@ CPPFLAGS += -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/ LDFLAGS += -L$(top_builddir)/lib -ifeq ($(UCLINUX),1) -CPPFLAGS += -D__UCLIBC__ -DUCLINUX -endif - ifeq ($(ANDROID),1) LDFLAGS += -L$(top_builddir)/lib/android_libpthread LDFLAGS += -L$(top_builddir)/lib/android_librt @@ -94,7 +73,7 @@ CHECK_TARGETS ?= $(addprefix check-,$(notdir $(patsubst %.c,%,$(sort $(wildcar CHECK_TARGETS := $(filter-out $(addprefix check-, $(FILTER_OUT_MAKE_TARGETS)), $(CHECK_TARGETS)) CHECK_HEADER_TARGETS ?= $(addprefix check-,$(notdir $(sort $(wildcard $(abs_srcdir)/*.h)))) CHECK ?= $(abs_top_srcdir)/tools/sparse/sparse-ltp -CHECK_NOFLAGS ?= $(abs_top_srcdir)/scripts/checkpatch.pl -f --no-tree --terse --no-summary --ignore CONST_STRUCT,VOLATILE,SPLIT_STRING +CHECK_NOFLAGS ?= $(abs_top_srcdir)/scripts/checkpatch.pl -f --no-tree --terse --no-summary --ignore CONST_STRUCT,VOLATILE,SPLIT_STRING,FILE_PATH_CHANGES SHELL_CHECK ?= $(abs_top_srcdir)/scripts/checkbashisms.pl --force --extra SHELL_CHECK_TARGETS ?= $(addprefix check-,$(notdir $(sort $(wildcard $(abs_srcdir)/*.sh)))) diff --git a/include/mk/env_pre.mk b/include/mk/env_pre.mk index f3621516..46d6abec 100755 --- a/include/mk/env_pre.mk +++ b/include/mk/env_pre.mk @@ -1,29 +1,11 @@ -# -# Make pre-include environment Makefile. -# -# Copyright (c) Linux Test Project, 2009-2020 -# Copyright (c) Cisco Systems Inc., 2009 -# -# 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. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Make pre-include environment Makefile. +# Copyright (c) Linux Test Project, 2009-2020 +# Copyright (c) Cisco Systems Inc., 2009 # Ngie Cooper, September 2009 # # This Makefile must be included first. NO IF'S, AND'S, OR BUT'S. -# # This sets the stage for all operations required within Makefiles. -# ifndef ENV_PRE_LOADED ENV_PRE_LOADED = 1 diff --git a/include/mk/features.mk.in b/include/mk/features.mk.in index 802ee0ba..fd93dc37 100755 --- a/include/mk/features.mk.in +++ b/include/mk/features.mk.in @@ -1,24 +1,6 @@ -# -# features.mk.in - feature tuning include Makefile. -# -# Copyright (C) 2010, Linux Test Project. -# -# 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. -# +# Copyright (c) Linux Test Project, 2008-2025 +# SPDX-License-Identifier: GPL-2.0-or-later # Ngie Cooper, October 2010 -# # Tools enable knobs WITH_EXPECT := @WITH_EXPECT@ @@ -36,22 +18,8 @@ WITH_METADATA_PDF := @WITH_METADATA_PDF@ # Test suite knobs -# Enable testcases/kernel/power_management's compile and install? -ifeq ($(UCLINUX),1) -WITH_POWER_MANAGEMENT_TESTSUITE := no -else -WITH_POWER_MANAGEMENT_TESTSUITE := yes -endif - # Enable testcases/open_posix_testsuite's compile and install? WITH_OPEN_POSIX_TESTSUITE := @WITH_OPEN_POSIX_TESTSUITE@ -# Enable testcases/realtime's compile and install? -ifeq ($(UCLINUX),1) -WITH_REALTIME_TESTSUITE := no -else -WITH_REALTIME_TESTSUITE := @WITH_REALTIME_TESTSUITE@ -endif - # Enable testcases/kernel/kvm compile and install? WITH_KVM_TESTSUITE := @WITH_KVM_TESTSUITE@ diff --git a/include/mk/functions.mk b/include/mk/functions.mk index e86dbccd..60dbed39 100755 --- a/include/mk/functions.mk +++ b/include/mk/functions.mk @@ -1,25 +1,8 @@ -# -# A Makefile with a collection of reusable functions. -# -# Copyright (c) Linux Test Project, 2009-2020 -# Copyright (c) Cisco Systems Inc., 2009 -# -# 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. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# A Makefile with a collection of reusable functions. +# Copyright (c) Linux Test Project, 2009-2020 +# Copyright (c) Cisco Systems Inc., 2009 # Ngie Cooper, July 2009 -# # Generate an install rule which also creates the install directory if needed # to avoid unnecessary bourne shell based for-loops and install errors, as well diff --git a/include/mk/generic_leaf_target.inc b/include/mk/generic_leaf_target.inc index 565a282b..7c685fea 100755 --- a/include/mk/generic_leaf_target.inc +++ b/include/mk/generic_leaf_target.inc @@ -1,24 +1,8 @@ -# -# Generic leaf rules include Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Generic leaf rules include Makefile. +# Copyright (c) Linux Test Project, 2017-2022 +# Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 -# # # generic_leaf_target diff --git a/include/mk/generic_leaf_target.mk b/include/mk/generic_leaf_target.mk index 908d0b00..c200803b 100755 --- a/include/mk/generic_leaf_target.mk +++ b/include/mk/generic_leaf_target.mk @@ -1,24 +1,8 @@ -# -# Generic leaf include Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Generic leaf include Makefile. +# Copyright (c) Linux Test Project, 2017 +# Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 -# include $(top_srcdir)/include/mk/env_post.mk include $(top_srcdir)/include/mk/generic_leaf_target.inc diff --git a/include/mk/generic_trunk_target.inc b/include/mk/generic_trunk_target.inc index 82aece7c..f9db7896 100755 --- a/include/mk/generic_trunk_target.inc +++ b/include/mk/generic_trunk_target.inc @@ -1,24 +1,8 @@ -# -# Generic trunk rules include Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2010-2021 +# Generic trunk rules include Makefile. +# Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 -# # # generic_trunk_target diff --git a/include/mk/generic_trunk_target.mk b/include/mk/generic_trunk_target.mk index 576b32db..e25f7bce 100755 --- a/include/mk/generic_trunk_target.mk +++ b/include/mk/generic_trunk_target.mk @@ -1,24 +1,8 @@ -# -# Generic trunk include Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Generic trunk include Makefile. +# Copyright (c) Linux Test Project, 2017 +# Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 -# include $(top_srcdir)/include/mk/env_post.mk include $(top_srcdir)/include/mk/generic_trunk_target.inc diff --git a/include/mk/gitignore.mk b/include/mk/gitignore.mk deleted file mode 100755 index 475206dc..00000000 --- a/include/mk/gitignore.mk +++ /dev/null @@ -1,51 +0,0 @@ -# -# gitignore generation include Makefile. -# -# Copyright (C) 2011, Linux Test Project. -# -# 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. -# -# Ngie Cooper, January 2011 -# - -CLEAN_TARGETS+= gitignore_clean - -BEFORE:= .gitignore-before - -AFTER:= .gitignore-after - -IGNORE_DIR_EXPR:= egrep -v "^$$(echo "$(AUTOCONFED_SUBDIRS)" | tr " " "|")" - -# NOTE: The underscore is used in place of a dash to avoid implicit rule -# evaluation in top-level Makefile. -.PHONY: gitignore_clean -gitignore_clean: - $(RM) -f $(BEFORE) $(AFTER) - -$(BEFORE): - $(MAKE) distclean - $(MAKE) ac-maintainer-clean - find . | $(IGNORE_DIR_EXPR) > $@ - -$(AFTER): - $(MAKE) autotools - ./configure --prefix=/dev/null - $(MAKE) all - find . | $(IGNORE_DIR_EXPR) > $@ - # Set everything in autoconf land back to a sane state. - $(MAKE) distclean - -.gitignore: | $(BEFORE) $(AFTER) - diff -u $(BEFORE) $(AFTER) | grep '^+' | sed -e 's,^\+,,g' > $@ diff --git a/include/mk/lib.mk b/include/mk/lib.mk index 3bf63bf9..cefb28c2 100755 --- a/include/mk/lib.mk +++ b/include/mk/lib.mk @@ -1,27 +1,8 @@ -# -# library include Makefile. -# -# Copyright (c) Linux Test Project, 2009-2020 -# Copyright (c) Cisco Systems Inc., 2009 -# -# 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. -# -# Ngie Cooper, July 2009 -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2009-2019 # Copyright (C) Cyril Hrubis 2012 -# +# Copyright (c) Cisco Systems Inc., 2009 +# Ngie Cooper, July 2009 # Makefile to include for libraries. diff --git a/include/mk/man.mk b/include/mk/man.mk index c94af61b..a642dfb3 100755 --- a/include/mk/man.mk +++ b/include/mk/man.mk @@ -1,24 +1,7 @@ -# -# Manpage include Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2010-2017 +# Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 -# ifeq ($(strip $(MANPREFIX)),) $(error $$(MANPREFIX) not defined) diff --git a/include/mk/module.mk b/include/mk/module.mk index 3bb7350f..3e97f012 100755 --- a/include/mk/module.mk +++ b/include/mk/module.mk @@ -1,27 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-or-later # 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 -# +# Copyright (c) Linux Test Project, 2014-2025 # Author: Alexey Kodanev # # Include it to build kernel modules. # REQ_VERSION_MAJOR and REQ_VERSION_PATCH must be defined beforehand. # +# FORCE_MODULES=1: Forcing to fail on error or missing kernel headers (e.g. for CI)). $(if $(REQ_VERSION_MAJOR),,$(error You must define REQ_VERSION_MAJOR)) -$(if $(REQ_VERSION_PATCH),,$(error You must define REQ_VERSION_MINOR)) +$(if $(REQ_VERSION_PATCH),,$(error You must define REQ_VERSION_PATCH)) + +define newline + + +endef +n := $(newline) ifeq ($(WITH_MODULES),no) SKIP := 1 @@ -36,8 +30,18 @@ SKIP ?= $(shell \ endif endif +$(info skip: $(SKIP), FORCE_MODULES: $(FORCE_MODULES)) ifneq ($(SKIP),0) MAKE_TARGETS := $(filter-out %.ko, $(MAKE_TARGETS)) +ifeq ($(FORCE_MODULES),1) +$(error Kernel modules not built!$(n)\ +Check that package for building kernel modules for $(LINUX_VERSION)\ +is installed and try again.$(n)\ +* openSUSE/SLES: kernel-default-devel$(n)\ +* Fedora/RHEL: kernel-devel/kernel-headers$(n)\ +* Debian/Ubuntu: linux-kbuild$(n)\ +You can build anyway by omitting FORCE_MODULES=1) +endif endif ifneq ($(filter install clean,$(MAKECMDGOALS)),) @@ -59,6 +63,10 @@ MODULE_SOURCES := $(patsubst %.ko,%.c,$(filter %.ko, $(MAKE_TARGETS))) .dep_modules: $(MODULE_SOURCES) @echo "Building modules: $(MODULE_SOURCES)" +ifneq ($(FORCE_MODULES),1) -$(MAKE) -C $(LINUX_DIR) M=$(abs_srcdir) +else + $(MAKE) -C $(LINUX_DIR) M=$(abs_srcdir) +endif rm -rf *.mod.c *.o *.ko.unsigned modules.order .tmp* .*.ko .*.cmd Module.symvers @touch .dep_modules diff --git a/include/mk/rules.mk b/include/mk/rules.mk index 517863c0..c7da6d37 100755 --- a/include/mk/rules.mk +++ b/include/mk/rules.mk @@ -1,3 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2020-2022 + target_rel_dir := $(if $(cwd_rel_from_top),$(cwd_rel_from_top)/,) %.o: %.S diff --git a/include/mk/sparse.mk b/include/mk/sparse.mk index a8692839..3390672c 100755 --- a/include/mk/sparse.mk +++ b/include/mk/sparse.mk @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2021 # Rules to make sparse tool(s) for inclusion in lib and testcases Makefiles SPARSE_DIR:= $(abs_top_builddir)/tools/sparse diff --git a/include/mk/testcases.mk b/include/mk/testcases.mk index 444020f1..2609535c 100755 --- a/include/mk/testcases.mk +++ b/include/mk/testcases.mk @@ -1,24 +1,6 @@ -# -# testcases include Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2009-2024 # Ngie Cooper, July 2009 -# include $(top_srcdir)/include/mk/env_pre.mk include $(top_srcdir)/include/mk/functions.mk @@ -44,8 +26,8 @@ LDLIBS += -lltp ifdef LTPLIBS -LTPLIBS_DIRS = $(addprefix $(abs_top_builddir)/libs/lib, $(LTPLIBS)) -LTPLIBS_FILES = $(addsuffix .a, $(addprefix $(abs_top_builddir)/libs/, $(foreach LIB,$(LTPLIBS),lib$(LIB)/lib$(LIB)))) +LTPLIBS_DIRS = $(addprefix $(abs_top_builddir)/libs/, $(LTPLIBS)) +LTPLIBS_FILES = $(addsuffix .a, $(addprefix $(abs_top_builddir)/libs/, $(foreach LIB,$(LTPLIBS),$(LIB)/lib$(LIB)))) MAKE_DEPS += $(LTPLIBS_FILES) @@ -61,7 +43,7 @@ else @$(MAKE) --no-print-directory -C "$(dir $@)" -f "$(subst $(abs_top_builddir),$(abs_top_srcdir),$(dir $@))/Makefile" all endif -LDFLAGS += $(addprefix -L$(top_builddir)/libs/lib, $(LTPLIBS)) +LDFLAGS += $(addprefix -L$(top_builddir)/libs/, $(LTPLIBS)) endif diff --git a/include/old/cleanup.c b/include/old/cleanup.c deleted file mode 100755 index 040dff85..00000000 --- a/include/old/cleanup.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Default cleanup logic because linux_syscall_numbers.h's need for cleanup - * and binutils bugs suck. - * - * Copyright (c) 2009 Cisco Systems, 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. - * - */ - -#ifndef __CLEANUP_C__ -#define __CLEANUP_C__ - -/* Did the user define a cleanup function? */ -#ifndef CLEANUP -#define USING_DUMMY_CLEANUP 1 -#define CLEANUP dummy_cleanup -#endif - -/* A freebie for defining the function prototype. */ -static void CLEANUP(void) __attribute__ ((unused)); - -#ifdef USING_DUMMY_CLEANUP -/* The stub function. Wewt.. */ -static void dummy_cleanup(void) -{ -} -#endif - -#endif diff --git a/include/old/old_checkpoint.h b/include/old/old_checkpoint.h index c8ffc92d..f91fef9f 100755 --- a/include/old/old_checkpoint.h +++ b/include/old/old_checkpoint.h @@ -32,14 +32,6 @@ #include "test.h" #include "tst_checkpoint_fn.h" -/* - * Checkpoint initializaton, must be done first. - * - * NOTE: tst_tmpdir() must be called beforehand. - */ -#define TST_CHECKPOINT_INIT(cleanup_fn) \ - tst_checkpoint_init(__FILE__, __LINE__, cleanup_fn) - #define TST_SAFE_CHECKPOINT_WAIT(cleanup_fn, id) \ tst_safe_checkpoint_wait(__FILE__, __LINE__, cleanup_fn, id, 0); diff --git a/include/old/old_module.h b/include/old/old_module.h index 496520d6..f49c9937 100755 --- a/include/old/old_module.h +++ b/include/old/old_module.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * Copyright (c) Linux Test Project, 2016-2024 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -34,6 +35,8 @@ #ifndef TST_MODULE #define TST_MODULE +#include + void tst_module_exists_(void (cleanup_fn)(void), const char *mod_name, char **mod_path); @@ -42,6 +45,9 @@ void tst_module_load_(void (cleanup_fn)(void), const char *mod_name, void tst_module_unload_(void (cleanup_fn)(void), const char *mod_name); +bool tst_module_signature_enforced_(void); +void tst_requires_module_signature_disabled_(void); + /* * Check module existence. * @@ -86,4 +92,31 @@ static inline void tst_module_unload(void (cleanup_fn)(void), const char *mod_na tst_module_unload_(cleanup_fn, mod_name); } +/** + * tst_requires_module_signature_disabled() - Check if enforced module signature. + * + * Module signature is enforced if module.sig_enforce=1 kernel parameter or + * CONFIG_MODULE_SIG_FORCE=y. + * + * return: Returns true if module signature is enforced false otherwise. + * + */ +static inline bool tst_module_signature_enforced(void) +{ + return tst_module_signature_enforced_(); +} + +/** + * tst_requires_module_signature_disabled() - Check if test needs to be skipped due + * enforced module signature. + * + * Skip test with tst_brk(TCONF) due module signature enforcement if + * module.sig_enforce=1 kernel parameter or CONFIG_MODULE_SIG_FORCE=y. + */ + +static inline void tst_requires_module_signature_disabled(void) +{ + tst_requires_module_signature_disabled_(); +} + #endif /* TST_MODULE */ diff --git a/include/old/safe_macros.h b/include/old/safe_macros.h index fb1d7a11..307843ab 100755 --- a/include/old/safe_macros.h +++ b/include/old/safe_macros.h @@ -150,7 +150,7 @@ #define SAFE_MOUNT(cleanup_fn, source, target, filesystemtype, \ mountflags, data) \ safe_mount(__FILE__, __LINE__, (cleanup_fn), (source), (target), \ - (filesystemtype), (mountflags), (data)) + (filesystemtype), (mountflags), (data), NULL) #define SAFE_UMOUNT(cleanup_fn, target) \ safe_umount(__FILE__, __LINE__, (cleanup_fn), (target)) diff --git a/include/old/test.h b/include/old/test.h index 834e92d9..306868fb 100755 --- a/include/old/test.h +++ b/include/old/test.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009-2013 Cyril Hrubis chrubis@suse.cz @@ -31,7 +31,6 @@ #include "tst_pid.h" #include "tst_cmd.h" #include "tst_cpu.h" -#include "tst_clone.h" #include "old_device.h" #include "old_tmpdir.h" #include "tst_minmax.h" @@ -65,23 +64,6 @@ /* strings to control tst_res output */ /* If not set, TOUT_VERBOSE_S is assumed */ -/* - * fork() can't be used on uClinux systems, so use FORK_OR_VFORK instead, - * which will run vfork() on uClinux. - * mmap() doesn't support MAP_PRIVATE on uClinux systems, so use - * MAP_PRIVATE_EXCEPT_UCLINUX instead, which will skip the option on uClinux. - * If MAP_PRIVATE really is required, the test can not be run on uClinux. - */ -#ifdef UCLINUX -# define FORK_OR_VFORK tst_vfork -# define MAP_PRIVATE_EXCEPT_UCLINUX 0 -/* tst_old_flush() + vfork() */ -pid_t tst_vfork(void); -#else -# define FORK_OR_VFORK tst_fork -# define MAP_PRIVATE_EXCEPT_UCLINUX MAP_PRIVATE -#endif - /* * Macro to use for making functions called only once in * multi-threaded tests such as init or cleanup function. @@ -184,10 +166,6 @@ extern int tst_count; /* lib/tst_sig.c */ void tst_sig(int fork_flag, void (*handler)(), void (*cleanup)()); -/* lib/self_exec.c */ -void maybe_run_child(void (*child)(), const char *fmt, ...); -int self_exec(const char *argv0, const char *fmt, ...); - /* lib/tst_mkfs.c * * @dev: path to a device diff --git a/include/old/usctest.h b/include/old/usctest.h index 9b9446d7..b984c343 100755 --- a/include/old/usctest.h +++ b/include/old/usctest.h @@ -34,16 +34,8 @@ #ifndef __USCTEST_H__ #define __USCTEST_H__ -/* - * Ensure that PATH_MAX is defined - */ -#ifndef PATH_MAX -#ifdef MAXPATHLEN -#define PATH_MAX MAXPATHLEN -#else -#define PATH_MAX 1024 -#endif -#endif +/* For PATH_MAX */ +#include /*********************************************************************** * The following globals are defined in parse_opts.c but must be @@ -68,20 +60,6 @@ extern int TEST_ERRNO; TEST_ERRNO = errno; \ } while (0) -/*********************************************************************** - * TEST_VOID: calls a system call - * - * parameters: - * SCALL = system call and parameters to execute - * - * Note: This is IDENTICAL to the TEST() macro except that it is intended - * for use with syscalls returning no values (void syscall()). The - * Typecasting nothing (void) into an unsigned integer causes compilation - * errors. - * - ***********************************************************************/ -#define TEST_VOID(SCALL) do { errno = 0; SCALL; TEST_ERRNO = errno; } while (0) - /*********************************************************************** * TEST_PAUSE: Pause for SIGUSR1 if the pause flag is set. * Just continue when signal comes in. diff --git a/include/safe_file_ops_fn.h b/include/safe_file_ops_fn.h index e8ed8538..223fb0d6 100755 --- a/include/safe_file_ops_fn.h +++ b/include/safe_file_ops_fn.h @@ -90,8 +90,4 @@ int safe_touch(const char *file, const int lineno, const char *pathname, mode_t mode, const struct timespec times[2]); -/* helper functions to setup overlayfs mountpoint */ -void create_overlay_dirs(void); -int mount_overlay(const char *file, const int lineno, int skip); - #endif /* SAFE_FILE_OPS_FN */ diff --git a/include/safe_macros_fn.h b/include/safe_macros_fn.h index d256091b..9a09c492 100755 --- a/include/safe_macros_fn.h +++ b/include/safe_macros_fn.h @@ -151,6 +151,9 @@ int safe_chown(const char *file, const int lineno, void (cleanup_fn)(void), int safe_fchown(const char *file, const int lineno, void (cleanup_fn)(void), int fd, uid_t owner, gid_t group); +int safe_lchown(const char *file, const int lineno, void (*cleanup_fn)(void), + const char *path, uid_t owner, gid_t group); + pid_t safe_wait(const char *file, const int lineno, void (cleanup_fn)(void), int *status); @@ -172,7 +175,7 @@ int safe_rename(const char *file, const int lineno, void (*cleanup_fn)(void), 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); + const void *data, int *is_fuse); int safe_umount(const char *file, const int lineno, void (*cleanup_fn)(void), const char *target); diff --git a/include/tst_af_alg.h b/include/tst_af_alg.h index 86df18eb..5c307ed0 100755 --- a/include/tst_af_alg.h +++ b/include/tst_af_alg.h @@ -3,10 +3,11 @@ * Copyright 2019 Google LLC * Copyright (c) Linux Test Project, 2021 */ + /** - * @file tst_af_alg.h + * DOC: tst_af_alg.h -- Kernel crypto algorithms (AF_ALG) helpers * - * Library for accessing kernel crypto algorithms via AF_ALG. + * Helpers for accessing kernel crypto algorithms via AF_ALG. * * See https://www.kernel.org/doc/html/latest/crypto/userspace-if.html * for more information about AF_ALG. @@ -19,21 +20,21 @@ #include /** - * Create an AF_ALG algorithm socket. + * tst_alg_create() - Create an AF_ALG algorithm socket. * * This creates an AF_ALG algorithm socket that is initially not bound to any * particular algorithm. On failure, tst_brk() is called with TCONF if the * kernel doesn't support AF_ALG, otherwise TBROK. * - * @return a new AF_ALG algorithm socket + * Return: a new AF_ALG algorithm socket */ int tst_alg_create(void); /** - * Bind an AF_ALG algorithm socket to an algorithm. + * tst_alg_bind_addr() - Bind an AF_ALG algorithm socket to an algorithm. * - * @param algfd An AF_ALG algorithm socket - * @param addr A structure which specifies the algorithm to use + * @algfd: An AF_ALG algorithm socket + * @addr: A structure which specifies the algorithm to use * * On failure, tst_brk() is called with TCONF if the kernel doesn't support the * specified algorithm, otherwise TBROK. @@ -41,11 +42,11 @@ int tst_alg_create(void); void tst_alg_bind_addr(int algfd, const struct sockaddr_alg *addr); /** - * Bind an AF_ALG algorithm socket to an algorithm. + * tst_alg_bind() - Bind an AF_ALG algorithm socket to an algorithm. * - * @param algfd An AF_ALG algorithm socket - * @param algtype The type of algorithm, such as "hash" or "skcipher" - * @param algname The name of the algorithm, such as "sha256" or "xts(aes)" + * @algfd: An AF_ALG algorithm socket + * @algtype: The type of algorithm, such as "hash" or "skcipher" + * @algname: The name of the algorithm, such as "sha256" or "xts(aes)" * * Like tst_alg_bind_addr(), except this just takes in the algorithm type and * name. The 'feat' and 'mask' fields are left 0. @@ -56,32 +57,32 @@ void tst_alg_bind_addr(int algfd, const struct sockaddr_alg *addr); void tst_alg_bind(int algfd, const char *algtype, const char *algname); /** - * Check for the availability of an algorithm. + * tst_try_alg() - Check for the availability of an algorithm. * - * @param algtype The type of algorithm, such as "hash" or "skcipher" - * @param algname The name of the algorithm, such as "sha256" or "xts(aes)" + * @algtype: The type of algorithm, such as "hash" or "skcipher" + * @algname: The name of the algorithm, such as "sha256" or "xts(aes)" * - * Return 0 if the algorithm is available, or errno if unavailable. + * Return: 0 if the algorithm is available, or errno if unavailable. */ int tst_try_alg(const char *algtype, const char *algname); /** - * Check for the availability of an algorithm. + * tst_have_alg() - Check for the availability of an algorithm. * - * @param algtype The type of algorithm, such as "hash" or "skcipher" - * @param algname The name of the algorithm, such as "sha256" or "xts(aes)" + * @algtype: The type of algorithm, such as "hash" or "skcipher" + * @algname: The name of the algorithm, such as "sha256" or "xts(aes)" * - * Return true if the algorithm is available, or false if unavailable + * Return: true if the algorithm is available, or false if unavailable * and call tst_res() with TCONF. If another error occurs, tst_brk() is called * with TBROK unless algorithm is disabled due FIPS mode (errno ELIBBAD). */ bool tst_have_alg(const char *algtype, const char *algname); /** - * Require the availability of an algorithm. + * tst_require_alg() - Require the availability of an algorithm. * - * @param algtype The type of algorithm, such as "hash" or "skcipher" - * @param algname The name of the algorithm, such as "sha256" or "xts(aes)" + * @algtype: The type of algorithm, such as "hash" or "skcipher" + * @algname: The name of the algorithm, such as "sha256" or "xts(aes)" * * If the algorithm is unavailable, tst_brk() is called with TCONF. * If another error occurs, tst_brk() is called with TBROK. @@ -89,20 +90,20 @@ bool tst_have_alg(const char *algtype, const char *algname); void tst_require_alg(const char *algtype, const char *algname); /** - * Assign a cryptographic key to an AF_ALG algorithm socket. + * tst_alg_setkey() - Assign a cryptographic key to an AF_ALG algorithm socket. * - * @param algfd An AF_ALG algorithm socket - * @param key Pointer to the key. If NULL, a random key is generated. - * @param keylen Length of the key in bytes + * @algfd: An AF_ALG algorithm socket + * @key: Pointer to the key. If NULL, a random key is generated. + * @keylen: Length of the key in bytes * * On failure, tst_brk() is called with TBROK. */ void tst_alg_setkey(int algfd, const uint8_t *key, unsigned int keylen); /** - * Create an AF_ALG request socket for the given algorithm socket. + * tst_alg_accept() - Create an AF_ALG request socket for the given algorithm socket. * - * @param algfd An AF_ALG algorithm socket + * @algfd: An AF_ALG algorithm socket * * This creates a request socket for the given algorithm socket, which must be * bound to an algorithm. The same algorithm socket can have many request @@ -112,35 +113,40 @@ void tst_alg_setkey(int algfd, const uint8_t *key, unsigned int keylen); * * On failure, tst_brk() is called with TBROK. * - * @return a new AF_ALG request socket + * Return: a new AF_ALG request socket */ int tst_alg_accept(int algfd); /** - * Set up an AF_ALG algorithm socket for the given algorithm w/ given key. + * tst_alg_setup() - Set up an AF_ALG algorithm socket for the given algorithm w/ given key. * - * @param algtype The type of algorithm, such as "hash" or "skcipher" - * @param algname The name of the algorithm, such as "sha256" or "xts(aes)" - * @param key The key to use (optional) - * @param keylen The length of the key in bytes (optional) + * @algtype: The type of algorithm, such as "hash" or "skcipher" + * @algname: The name of the algorithm, such as "sha256" or "xts(aes)" + * @key: The key to use (optional) + * @keylen: The length of the key in bytes (optional) * * This is a helper function which creates an AF_ALG algorithm socket, binds it * to the specified algorithm, and optionally sets a key. If keylen is 0 then * no key is set; otherwise if key is NULL a key of the given length is randomly * generated and set; otherwise the given key is set. * - * @return the AF_ALG algorithm socket that was set up + * Return: the AF_ALG algorithm socket that was set up */ int tst_alg_setup(const char *algtype, const char *algname, const uint8_t *key, unsigned int keylen); /** - * Set up an AF_ALG request socket for the given algorithm w/ given key. + * tst_alg_setup_reqfd() - Set up an AF_ALG request socket for the given algorithm w/ given key. + * + * @algtype: The type of algorithm, such as "hash" or "skcipher" + * @algname: The name of the algorithm, such as "sha256" or "xts(aes)" + * @key: The key to use (optional) + * @keylen: The length of the key in bytes (optional) * * This is like tst_alg_setup(), except this returns a request fd instead of the * alg fd. The alg fd is closed, so it doesn't need to be kept track of. * - * @return the AF_ALG request socket that was set up + * Return: the AF_ALG request socket that was set up */ int tst_alg_setup_reqfd(const char *algtype, const char *algname, const uint8_t *key, unsigned int keylen); @@ -166,11 +172,11 @@ struct tst_alg_sendmsg_params { }; /** - * Send some data to an AF_ALG request socket, including control data. - * @param reqfd An AF_ALG request socket - * @param data The data to send - * @param datalen The length of data in bytes - * @param params Specification of the control data to send + * tst_alg_sendmsg() - Send some data to an AF_ALG request socket, including control data. + * @reqfd: An AF_ALG request socket + * @data: The data to send + * @datalen: The length of data in bytes + * @params: Specification of the control data to send * * On failure, tst_brk() is called with TBROK. */ diff --git a/include/tst_ansi_color.h b/include/tst_ansi_color.h index 770bf46d..376d4ad6 100755 --- a/include/tst_ansi_color.h +++ b/include/tst_ansi_color.h @@ -4,14 +4,17 @@ #ifndef TST_ANSI_COLOR_H__ #define TST_ANSI_COLOR_H__ + /* * NOTE: these colors should match colors defined in tst_flag2color() in * testcases/lib/tst_ansi_color.sh */ + #define ANSI_COLOR_BLUE "\033[1;34m" #define ANSI_COLOR_GREEN "\033[1;32m" #define ANSI_COLOR_MAGENTA "\033[1;35m" #define ANSI_COLOR_RED "\033[1;31m" +#define ANSI_COLOR_WHITE "\033[1;37m" #define ANSI_COLOR_YELLOW "\033[1;33m" #define ANSI_COLOR_RESET "\033[0m" diff --git a/include/tst_atomic.h b/include/tst_atomic.h index 061cd3dc..9d255bc4 100755 --- a/include/tst_atomic.h +++ b/include/tst_atomic.h @@ -1,81 +1,47 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright (c) 2016 Cyril Hrubis - */ - -/* The LTP library has some of its own atomic synchronisation primitives - * contained in this file. Generally speaking these should not be used - * directly in tests for synchronisation, instead use tst_checkpoint.h, - * tst_fuzzy_sync.h or the POSIX library. - * - * Notes on compile and runtime memory barriers and atomics. - * - * Within the LTP library we have three concerns when accessing variables - * shared by multiple threads or processes: - * - * (1) Removal or reordering of accesses by the compiler. - * (2) Atomicity of addition. - * (3) LOAD-STORE ordering between threads. - * - * The first (1) is the most likely to cause an error if not properly - * handled. We avoid it by using volatile variables and statements which will - * not be removed or reordered by the compiler during optimisation. This includes - * the __atomic and __sync intrinsics and volatile asm statements marked with - * "memory" as well as variables marked with volatile. - * - * On any platform Linux is likely to run on, a LOAD (fetch) or STORE of a - * 32-bit integer will be atomic. However fetching and adding to a variable is - * quite likely not; so for (2) we need to ensure we use atomic addition. - * - * Finally, for tst_fuzzy_sync at least, we need to ensure that LOADs and - * STOREs of any shared variables (including non-atomics) that are made - * between calls to tst_fzsync_wait are completed (globally visible) before - * tst_fzsync_wait completes. For this, runtime memory and instruction - * barriers are required in addition to compile time. - * - * We use full sequential ordering (__ATOMIC_SEQ_CST) for the sake of - * simplicity. LTP tests tend to be syscall heavy so any performance gain from - * using a weaker memory model is unlikely to result in a relatively large - * performance improvement while at the same time being a potent source of - * confusion. - * - * Likewise, for the fallback ASM, the simplest "definitely will work, always" - * approach is preferred over anything more performant. - * - * Also see Documentation/memory-barriers.txt in the kernel tree and - * https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html - * terminology may vary between sources. + * Copyright (c) 2025 Li Wang */ #ifndef TST_ATOMIC_H__ #define TST_ATOMIC_H__ +#include #include "config.h" +typedef int32_t tst_atomic_t; + #if HAVE_ATOMIC_MEMORY_MODEL == 1 -static inline int tst_atomic_add_return(int i, int *v) + +/* Use __atomic built-ins (GCC >= 4.7, Clang >= 3.1), with sequential consistency. */ + +static inline int tst_atomic_add_return(int32_t i, tst_atomic_t *v) { return __atomic_add_fetch(v, i, __ATOMIC_SEQ_CST); } -static inline int tst_atomic_load(int *v) +static inline int32_t tst_atomic_load(tst_atomic_t *v) { return __atomic_load_n(v, __ATOMIC_SEQ_CST); } -static inline void tst_atomic_store(int i, int *v) +static inline void tst_atomic_store(int32_t i, tst_atomic_t *v) { __atomic_store_n(v, i, __ATOMIC_SEQ_CST); } #elif HAVE_SYNC_ADD_AND_FETCH == 1 -static inline int tst_atomic_add_return(int i, int *v) + +/* Use __sync built-ins (GCC >= 4.1), with explicit memory barriers. */ + +static inline int tst_atomic_add_return(int32_t i, tst_atomic_t *v) { return __sync_add_and_fetch(v, i); } -static inline int tst_atomic_load(int *v) +static inline int32_t tst_atomic_load(tst_atomic_t *v) { - int ret; + tst_atomic_t ret; __sync_synchronize(); ret = *v; @@ -83,252 +49,25 @@ static inline int tst_atomic_load(int *v) return ret; } -static inline void tst_atomic_store(int i, int *v) +static inline void tst_atomic_store(int32_t i, tst_atomic_t *v) { __sync_synchronize(); *v = i; __sync_synchronize(); } -#elif defined(__i386__) || defined(__x86_64__) -# define LTP_USE_GENERIC_LOAD_STORE_ASM 1 - -static inline int tst_atomic_add_return(int i, int *v) -{ - int __ret = i; - - /* - * taken from arch/x86/include/asm/cmpxchg.h - */ - asm volatile ("lock; xaddl %0, %1\n" - : "+r" (__ret), "+m" (*v) : : "memory", "cc"); - - return i + __ret; -} - -#elif defined(__powerpc__) || defined(__powerpc64__) -static inline int tst_atomic_add_return(int i, int *v) -{ - int t; - - /* taken from arch/powerpc/include/asm/atomic.h */ - asm volatile( - " sync\n" - "1: lwarx %0,0,%2 # atomic_add_return\n" - " add %0,%1,%0\n" - " stwcx. %0,0,%2 \n" - " bne- 1b\n" - " sync\n" - : "=&r" (t) - : "r" (i), "r" (v) - : "cc", "memory"); - - return t; -} - -static inline int tst_atomic_load(int *v) -{ - int ret; - - asm volatile("sync\n" : : : "memory"); - ret = *v; - asm volatile("sync\n" : : : "memory"); - - return ret; -} - -static inline void tst_atomic_store(int i, int *v) -{ - asm volatile("sync\n" : : : "memory"); - *v = i; - asm volatile("sync\n" : : : "memory"); -} - -#elif defined(__s390__) || defined(__s390x__) -# define LTP_USE_GENERIC_LOAD_STORE_ASM 1 - -static inline int tst_atomic_add_return(int i, int *v) -{ - int old_val, new_val; - - /* taken from arch/s390/include/asm/atomic.h */ - asm volatile( - " l %0,%2\n" - "0: lr %1,%0\n" - " ar %1,%3\n" - " cs %0,%1,%2\n" - " jl 0b" - : "=&d" (old_val), "=&d" (new_val), "+Q" (*v) - : "d" (i) - : "cc", "memory"); - - return old_val + i; -} - -#elif defined(__arc__) - -/*ARCv2 defines the smp barriers */ -#ifdef __ARC700__ -#define smp_mb() asm volatile("" : : : "memory") #else -#define smp_mb() asm volatile("dmb 3\n" : : : "memory") +# error "Your compiler does not support atomic operations (__atomic or __sync)" #endif -static inline int tst_atomic_add_return(int i, int *v) -{ - unsigned int val; - - smp_mb(); - - asm volatile( - "1: llock %[val], [%[ctr]] \n" - " add %[val], %[val], %[i] \n" - " scond %[val], [%[ctr]] \n" - " bnz 1b \n" - : [val] "=&r" (val) - : [ctr] "r" (v), - [i] "ir" (i) - : "cc", "memory"); - - smp_mb(); - - return val; -} - -static inline int tst_atomic_load(int *v) -{ - int ret; - - smp_mb(); - ret = *v; - smp_mb(); - - return ret; -} - -static inline void tst_atomic_store(int i, int *v) -{ - smp_mb(); - *v = i; - smp_mb(); -} - -#elif defined (__aarch64__) -static inline int tst_atomic_add_return(int i, int *v) -{ - unsigned long tmp; - int result; - - __asm__ __volatile__( -" prfm pstl1strm, %2 \n" -"1: ldaxr %w0, %2 \n" -" add %w0, %w0, %w3 \n" -" stlxr %w1, %w0, %2 \n" -" cbnz %w1, 1b \n" -" dmb ish \n" - : "=&r" (result), "=&r" (tmp), "+Q" (*v) - : "Ir" (i) - : "memory"); - - return result; -} - -/* We are using load and store exclusive (ldaxr & stlxr) instructions to try - * and help prevent the tst_atomic_load and, more likely, tst_atomic_store - * functions from interfering with tst_atomic_add_return which takes advantage - * of exclusivity. It is not clear if this is a good idea or not, but does - * mean that all three functions are very similar. - */ -static inline int tst_atomic_load(int *v) -{ - int ret; - unsigned long tmp; - - asm volatile("//atomic_load \n" - " prfm pstl1strm, %[v] \n" - "1: ldaxr %w[ret], %[v] \n" - " stlxr %w[tmp], %w[ret], %[v] \n" - " cbnz %w[tmp], 1b \n" - " dmb ish \n" - : [tmp] "=&r" (tmp), [ret] "=&r" (ret), [v] "+Q" (*v) - : : "memory"); - - return ret; -} - -static inline void tst_atomic_store(int i, int *v) -{ - unsigned long tmp; - - asm volatile("//atomic_store \n" - " prfm pstl1strm, %[v] \n" - "1: ldaxr %w[tmp], %[v] \n" - " stlxr %w[tmp], %w[i], %[v] \n" - " cbnz %w[tmp], 1b \n" - " dmb ish \n" - : [tmp] "=&r" (tmp), [v] "+Q" (*v) - : [i] "r" (i) - : "memory"); -} - -#elif defined(__sparc__) && defined(__arch64__) -# define LTP_USE_GENERIC_LOAD_STORE_ASM 1 -static inline int tst_atomic_add_return(int i, int *v) -{ - int ret, tmp; - - /* Based on arch/sparc/lib/atomic_64.S with the exponential backoff - * function removed because we are unlikely to have a large (>= 16?) - * number of cores continuously trying to update one variable. - */ - asm volatile("/*atomic_add_return*/ \n" - "1: ldsw [%[v]], %[ret]; \n" - " add %[ret], %[i], %[tmp]; \n" - " cas [%[v]], %[ret], %[tmp]; \n" - " cmp %[ret], %[tmp]; \n" - " bne,pn %%icc, 1b; \n" - " nop; \n" - " add %[ret], %[i], %[ret]; \n" - : [ret] "=r&" (ret), [tmp] "=r&" (tmp) - : [i] "r" (i), [v] "r" (v) - : "memory", "cc"); - - return ret; -} - -#else /* HAVE_SYNC_ADD_AND_FETCH == 1 */ -# error Your compiler does not provide __atomic_add_fetch, __sync_add_and_fetch \ - and an LTP implementation is missing for your architecture. -#endif - -#ifdef LTP_USE_GENERIC_LOAD_STORE_ASM -static inline int tst_atomic_load(int *v) -{ - int ret; - - asm volatile("" : : : "memory"); - ret = *v; - asm volatile("" : : : "memory"); - - return ret; -} - -static inline void tst_atomic_store(int i, int *v) -{ - asm volatile("" : : : "memory"); - *v = i; - asm volatile("" : : : "memory"); -} -#endif - -static inline int tst_atomic_inc(int *v) +static inline int tst_atomic_inc(tst_atomic_t *v) { return tst_atomic_add_return(1, v); } -static inline int tst_atomic_dec(int *v) +static inline int tst_atomic_dec(tst_atomic_t *v) { return tst_atomic_add_return(-1, v); } -#endif /* TST_ATOMIC_H__ */ +#endif /* TST_ATOMIC_H__ */ diff --git a/include/tst_buffers.h b/include/tst_buffers.h index b5f355f0..bd1a112a 100755 --- a/include/tst_buffers.h +++ b/include/tst_buffers.h @@ -3,69 +3,95 @@ * Copyright (c) 2019 Cyril Hrubis */ +/** + * DOC: Guarded buffers introduction + * + * Guarded buffer has a page with PROT_NONE allocated right before the start of + * the buffer and canary after the end of the buffer. That means that any + * memory access before the buffer ends with EFAULT or SEGFAULT and any write + * after the end of the buffer will be detected because it would overwrite the + * canaries. + * + * It should be used for all buffers passed to syscalls to make sure off-by-one + * buffer accesses does not happen. + */ + #ifndef TST_BUFFERS_H__ #define TST_BUFFERS_H__ -/* +/** + * struct tst_buffers - A guarded buffer description for allocator. + * * Buffer description consist of a pointer to a pointer and buffer type/size * encoded as a different structure members. * - * Only one of the size and iov_sizes can be set at a time. + * @ptr: A pointer to the pointer to buffer. This is dereferenced and set by the + * allocator. + * @size: A buffer size in bytes. Only one of size and iov_sizes can be set. + * @iov_sizes: An -1 terminated array of sizes used to construct a + * struct iovec buffers. + * @str: If size is zero and iov_sizes is NULL this string is going to be + * copied into the buffer. */ struct tst_buffers { - /* - * This pointer points to a buffer pointer. - */ void *ptr; - /* - * Buffer size. - */ size_t size; - /* - * Array of iov buffer sizes terminated by -1. - */ int *iov_sizes; - /* - * If size and iov_sizes is NULL this is the string we want to strdup() - * into the buffer. - */ char *str; }; -/* - * Allocates buffers based on the tst_buffers structure. +/** + * tst_buffers_alloc() - Allocates buffers based on the tst_buffers structure. * - * @bufs NULL terminated array of test buffer descriptions. + * @bufs: A NULL terminated array of test buffer descriptions. * - * This is called from the test library if the tst_test->bufs pointer is set. + * This is called from the test library if the tst_test.bufs pointer is set. */ void tst_buffers_alloc(struct tst_buffers bufs[]); -/* - * strdup() that callls tst_alloc(). +/** + * tst_strdup() - Copies a string into a newly allocated guarded buffer. + * + * @str: A string to be duplicated. + * return: A pointer to the string duplicated in a guarded buffer. + * + * Allocates a buffer with tst_alloc() and copies the string into it. */ char *tst_strdup(const char *str); -/* - * Allocates size bytes, returns pointer to the allocated buffer. +/** + * tst_alloc() - Allocates a guarded buffer. + * + * @size: A size of the buffer. + * return: A newly allocated guarded buffer. */ void *tst_alloc(size_t size); -/* - * Printf into a guarded buffer. +/** + * tst_aprintf() - Printf into a newly allocated guarded buffer. + * + * @fmt: A printf-like format. + * @...: A printf-like parameters. + * return: A newly allocated buffer. + * + * Allocates a buffer with tst_alloc() then prints the data into it. */ char *tst_aprintf(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -/* - * Allocates iovec structure including the buffers. +/** + * tst_iovec_alloc() - Allocates a complete iovec structure. * - * @sizes -1 terminated array of buffer sizes. + * @sizes: A -1 terminated array of buffer sizes. + * return: Newly allocated iovec structure. */ struct iovec *tst_iovec_alloc(int sizes[]); -/* - * Frees all allocated buffers. +/** + * tst_free_all() - Frees all allocated buffers. + * + * It's important to free all guarded buffers because the canaries after the + * buffer are checked only when the buffer is being freed. * * This is called at the end of the test automatically. */ diff --git a/include/tst_capability.h b/include/tst_capability.h index 6067804a..ccf4bd77 100755 --- a/include/tst_capability.h +++ b/include/tst_capability.h @@ -2,8 +2,9 @@ /* * Copyright (c) 2019 Richard Palethorpe */ + /** - * @file tst_capability.h + * DOC: Capabilities introduction * * Limited capability operations without libcap. */ @@ -15,22 +16,51 @@ #include "lapi/capability.h" -#define TST_CAP_DROP 1 -#define TST_CAP_REQ (1 << 1) - -#define TST_CAP(action, capability) {action, capability, #capability} +/** + * enum tst_cap_act - A capability action masks. + * + * @TST_CAP_DROP: Drop capabilities. + * @TST_CAP_REQ: Add capabilities. + */ +enum tst_cap_act { + TST_CAP_DROP = 1, + TST_CAP_REQ = (1 << 1) +}; +/** + * struct tst_cap_user_header - Kernel capget(), capset() syscall header. + * + * @version: A capability API version. + * @pid: A process to operate on. + */ struct tst_cap_user_header { uint32_t version; int pid; }; +/** + * struct tst_cap_user_data - Kernel capset(), capget() syscall payload. + * + * @effective: A capability effective set. + * @permitted: A capability permitted set. + * @inheritable: A capability inheritable set. + */ struct tst_cap_user_data { uint32_t effective; uint32_t permitted; uint32_t inheritable; }; +/** + * struct tst_cap - A capability to alter. + * + * @action: What should we do, i.e. drop or add a capability. + * @id: A capability id. + * @name: A capability name. + * + * This structure is usually constructed with the TST_CAP() macro so that the + * name is created automatically. + */ struct tst_cap { uint32_t action; uint32_t id; @@ -38,25 +68,43 @@ struct tst_cap { }; /** - * Get the capabilities as decided by hdr. + * TST_CAP() - Create a struct tst_cap entry. * - * Note that the memory pointed to by data should be large enough to store two - * structs. + * @action: What should we do, i.e. drop or add capability. + * @capability: A capability id, e.g. CAP_BPF. + */ +#define TST_CAP(action, capability) {action, capability, #capability} + +/** + * tst_capget() - Get the capabilities as decided by hdr. + * + * @hdr: A capability user header stores a pid to operate on and which + * capability API version is used. + * @data: A memory to store the capabilities to. The memory pointed to by data + * should be large enough to store two structs. + * + * return: Returns 0 on success, -1 on a failure and sets errno. */ int tst_capget(struct tst_cap_user_header *hdr, struct tst_cap_user_data *data); /** - * Set the capabilities as decided by hdr and data + * tst_capset() - Set the capabilities as decided by hdr and data * - * Note that the memory pointed to by data should be large enough to store two - * structs. + * @hdr: A capability user header stores a pid to operate on and which + * capability API version is used. + * @data: A memory to store the capabilities to. The memory pointed to by data + * should be large enough to store two structs. + * + * return: Returns 0 on success, -1 on a failure and sets errno. */ int tst_capset(struct tst_cap_user_header *hdr, const struct tst_cap_user_data *data); /** - * Add, check or remove a capability + * tst_cap_action() - Add, check or remove a capability. + * + * @cap: An {} terminated array of capabilities to alter. * * It will attempt to drop or add capability to the effective set. It will * try to detect if this is needed and whether it can or can't be done. If it @@ -71,13 +119,17 @@ void tst_cap_action(struct tst_cap *cap); /** - * Add, check or remove a capabilities + * tst_cap_setup() - Add, check or remove a capabilities. + * + * @cap: An {} terminated array of capabilities to alter. + * @action_mask: Decides which actions are done, i.e. only drop caps, add them + * or both. * * Takes a NULL terminated array of structs which describe whether some * capabilities are needed or not and mask that determines subset of the * actions to be performed. Loops over the array and if mask matches the * element action it's passed to tst_cap_action(). */ -void tst_cap_setup(struct tst_cap *cap, unsigned int action_mask); +void tst_cap_setup(struct tst_cap *cap, enum tst_cap_act action_mask); #endif /* TST_CAPABILITY_H */ diff --git a/include/tst_cgroup.h b/include/tst_cgroup.h index be14d07c..0f0f44ec 100755 --- a/include/tst_cgroup.h +++ b/include/tst_cgroup.h @@ -5,8 +5,6 @@ * Copyright (c) 2020-2021 SUSE LLC */ /*\ - * [Description] - * * The LTP CGroups API tries to present a consistent interface to the * many possible CGroup configurations a system could have. * @@ -104,6 +102,7 @@ struct tst_cg_opts { * directory as opposed to the default pid of the calling process. */ int test_pid; + int needs_nsdelegate; }; /* A Control Group in LTP's aggregated hierarchy */ @@ -255,4 +254,8 @@ int safe_cg_occursin(const char *file, const int lineno, const char *const file_name, const char *const needle); +int tst_cg_memory_recursiveprot(struct tst_cg_group *cg); + +void tst_check_rt_group_sched_support(void); + #endif /* TST_CGROUP_H */ diff --git a/include/tst_checkpoint.h b/include/tst_checkpoint.h index 1b6911d7..f202dd03 100755 --- a/include/tst_checkpoint.h +++ b/include/tst_checkpoint.h @@ -1,5 +1,18 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright (c) 2016 Cyril Hrubis +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2016-2025 Cyril Hrubis + */ + +/** + * DOC: Checkpoints introduction + * + * Checkpoints implements a futex based synchronization primitive for threads + * and processes. When a process calls wait function its execution is suspended + * until wake is called for a corresponding checkpoint. Checkpoints are + * numbered from 0 and process can use at least hundred of them. + * + * In order to use checkpoints the test must set the tst_test.needs_checkpoints + * flag. */ #ifndef TST_CHECKPOINT__ @@ -7,23 +20,70 @@ #include "tst_checkpoint_fn.h" +/** + * TST_CHECKPOINT_WAIT() - Waits for a checkpoint. + * + * @id: A checkpoint id a positive integer. + * + * Suspends thread/process execution until it's woken up with a wake. The call + * does not wait indefinitely it gives up after 10 seconds. If an error + * happened or timeout was reached the function calls tst_brk(TBROK, ...) which + * exits the test. + */ #define TST_CHECKPOINT_WAIT(id) \ tst_safe_checkpoint_wait(__FILE__, __LINE__, NULL, id, 0) +/** + * TST_CHECKPOINT_WAIT2() - Waits for a checkpoint. + * + * @id: A checkpoint id a positive integer. + * @msec_timeout: A timeout. + * + * Suspends thread/process execution until it's woken up with a wake. If an + * error happened or timeout was reached the function calls tst_brk(TBROK, ...) + * which exits the test. + */ #define TST_CHECKPOINT_WAIT2(id, msec_timeout) \ tst_safe_checkpoint_wait(__FILE__, __LINE__, NULL, id, msec_timeout) +/** + * TST_CHECKPOINT_WAKE() - Wakes up a checkpoint. + * + * @id: A checkpoint id a positive integer. + * + * Wakes up a process suspended on a checkpoint and retries if there is no + * process suspended on the checkpoint yet. The call does not retry + * indefinitely but gives up after 10 seconds. If an error happened or timeout + * was reached the function calls tst_brk(TBROK, ...) which exits the test. + */ #define TST_CHECKPOINT_WAKE(id) \ tst_safe_checkpoint_wake(__FILE__, __LINE__, NULL, id, 1) +/** + * TST_CHECKPOINT_WAKE2() - Wakes up several checkpoints. + * + * @id: A checkpoint id a positive integer. + * @nr_wake: A number of processes to wake. + * + * Wakes up nr_wake processes suspended on a checkpoint and retries if there + * wasn't enough process suspended on the checkpoint yet. The call does not + * retry indefinitely but gives up if it does not wake nr_wake processes after + * 10 seconds. If an error happened or timeout was reached the function calls + * tst_brk(TBROK, ...) which exits the test. + */ #define TST_CHECKPOINT_WAKE2(id, nr_wake) \ tst_safe_checkpoint_wake(__FILE__, __LINE__, NULL, id, nr_wake) +/** + * TST_CHECKPOINT_WAKE_AND_WAIT() - Wakes up a checkpoint and immediately waits on it. + * + * @id: A checkpoint id a positive integer. + * + * This is a combination of TST_CHECKPOINT_WAKE() and TST_CHECKPOINT_WAIT(). + */ #define TST_CHECKPOINT_WAKE_AND_WAIT(id) do { \ tst_safe_checkpoint_wake(__FILE__, __LINE__, NULL, id, 1); \ tst_safe_checkpoint_wait(__FILE__, __LINE__, NULL, id, 0); \ } while (0) -extern const char *tst_ipc_path; - #endif /* TST_CHECKPOINT__ */ diff --git a/include/tst_checkpoint_fn.h b/include/tst_checkpoint_fn.h index 3a010d61..e061e00b 100755 --- a/include/tst_checkpoint_fn.h +++ b/include/tst_checkpoint_fn.h @@ -1,18 +1,11 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright (c) 2015-2016 Cyril Hrubis +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2015-2025 Cyril Hrubis */ #ifndef TST_CHECKPOINT_FN__ #define TST_CHECKPOINT_FN__ -/* - * Checkpoint initializaton, must be done first. - * - * NOTE: tst_tmpdir() must be called beforehand. - */ -void tst_checkpoint_init(const char *file, const int lineno, - void (*cleanup_fn)(void)); - /* * Waits for wakeup. * diff --git a/include/tst_clocks.h b/include/tst_clocks.h index 06d2d03b..69251d5d 100755 --- a/include/tst_clocks.h +++ b/include/tst_clocks.h @@ -20,15 +20,34 @@ int tst_clock_settime(clockid_t clk_id, struct timespec *ts); */ const char *tst_clock_name(clockid_t clk_id); +/* + * Returns timestamp (seconds passed) for the specified clock. + * TBROKs on error. + */ +time_t tst_clock_get_timestamp(clockid_t clk_id); + /* * Returns current system time for file/IPC operations, which may slightly lag - * behind time() return values. + * behind time() return values. Meant to be used as lower bound in atime/mtime + * checks. * * The reason for this is that the time() syscall reads the nanosecond timer at * the time of the call and adds it to the kernel current time, because of that * accumulation may cause it jump one second ahead compared to the kernel time * stamp that is used for IPC and filesystems. */ -time_t tst_get_fs_timestamp(void); +time_t tst_fs_timestamp_start(void); + +/* + * Returns current system time for file/IPC operation, using clock + * which has higher precision. Meant to be used as higher bound in atime/mtime + * checks. + * + * The reason for separate start/end functions is to cover features like + * multigrain timestamps, which update atime/mtime using more precise clock. + */ +time_t tst_fs_timestamp_end(void); + +int tst_get_max_clocks(void); #endif /* TST_CLOCKS__ */ diff --git a/include/tst_clone.h b/include/tst_clone.h index 7b278dfa..a07689c7 100755 --- a/include/tst_clone.h +++ b/include/tst_clone.h @@ -5,11 +5,14 @@ #ifndef TST_CLONE_H__ #define TST_CLONE_H__ +#include + #ifdef TST_TEST_H__ /* The parts of clone3's clone_args we support */ struct tst_clone_args { uint64_t flags; + uint64_t pidfd; uint64_t exit_signal; uint64_t cgroup; }; @@ -39,10 +42,6 @@ int ltp_clone(unsigned long flags, int (*fn)(void *arg), void *arg, size_t stack_size, void *stack); int ltp_clone7(unsigned long flags, int (*fn)(void *arg), void *arg, size_t stack_size, void *stack, ...); -int ltp_clone_alloc(unsigned long clone_flags, int (*fn)(void *arg), - void *arg, size_t stacksize); -int ltp_clone_quick(unsigned long clone_flags, int (*fn)(void *arg), - void *arg); void *ltp_alloc_stack(size_t size); #define clone(...) (use_the_ltp_clone_functions__do_not_use_clone) diff --git a/include/tst_cmd.h b/include/tst_cmd.h index 1f39f690..93982564 100755 --- a/include/tst_cmd.h +++ b/include/tst_cmd.h @@ -18,14 +18,16 @@ enum tst_cmd_flags { /* * vfork() + execvp() specified program. - * @argv: a list of two (at least program name + NULL) or more pointers that + * + * @param argv A list of two (at least program name + NULL) or more pointers that * represent the argument list to the new program. The array of pointers * must be terminated by a NULL pointer. - * @stdout_fd: file descriptor where to redirect stdout. Set -1 if + * @param stdout_fd File descriptor where to redirect stdout. Set -1 if * redirection is not needed. - * @stderr_fd: file descriptor where to redirect stderr. Set -1 if + * @param stderr_fd File descriptor where to redirect stderr. Set -1 if * redirection is not needed. - * @flags: enum tst_cmd_flags + * @param flags enum tst_cmd_flags. + * @return The exit status of the program. */ int tst_cmd_fds_(void (cleanup_fn)(void), const char *const argv[], @@ -33,12 +35,15 @@ int tst_cmd_fds_(void (cleanup_fn)(void), int stderr_fd, enum tst_cmd_flags flags); -/* Executes tst_cmd_fds() and redirects its output to a file - * @stdout_path: path where to redirect stdout. Set NULL if redirection is +/* + * Executes tst_cmd_fds() and redirects its output to a file. + * + * @param stdout_path Path where to redirect stdout. Set NULL if redirection is * not needed. - * @stderr_path: path where to redirect stderr. Set NULL if redirection is + * @param stderr_path Path where to redirect stderr. Set NULL if redirection is * not needed. - * @flags: enum tst_cmd_flags + * @param flags enum tst_cmd_flags. + * @return The exit status of the program. */ int tst_cmd_(void (cleanup_fn)(void), const char *const argv[], @@ -87,7 +92,7 @@ static inline int tst_cmd(void (cleanup_fn)(void), #endif /* Wrapper function for system(3), ignorcing SIGCHLD signal. - * @command: the command to be run. + * @param command The command to be run. */ int tst_system(const char *command); diff --git a/include/tst_common.h b/include/tst_common.h index 520cca72..47322814 100755 --- a/include/tst_common.h +++ b/include/tst_common.h @@ -13,6 +13,8 @@ #define LTP_ATTRIBUTE_UNUSED __attribute__((unused)) #define LTP_ATTRIBUTE_UNUSED_RESULT __attribute__((warn_unused_result)) +#define LTP_VAR_USED(p) asm volatile("" :: "m"(p)); p + #ifndef ARRAY_SIZE # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif @@ -77,10 +79,7 @@ #define TST_BUILD_BUG_ON(condition) \ do { ((void)sizeof(char[1 - 2 * !!(condition)])); } while (0) -#define TST_BRK_SUPPORTS_ONLY_TCONF_TBROK(condition) \ - TST_BUILD_BUG_ON(condition) - -#define TST_RES_SUPPORTS_TCONF_TFAIL_TINFO_TPASS_TWARN(condition) \ +#define TST_RES_SUPPORTS_TCONF_TDEBUG_TFAIL_TINFO_TPASS_TWARN(condition) \ TST_BUILD_BUG_ON(condition) /* stringification */ diff --git a/include/tst_crypto.h b/include/tst_crypto.h index ae406bd0..4511adf2 100755 --- a/include/tst_crypto.h +++ b/include/tst_crypto.h @@ -3,9 +3,9 @@ */ /** - * @file tst_crypto.h + * DOC: tst_crypto.h -- kernel's crypto layer helpers * - * Library for interacting with kernel's crypto layer using the netlink + * Helpers for interacting with kernel's crypto layer using the netlink * interface. */ @@ -13,86 +13,31 @@ #define TST_CRYPTO_H #include "lapi/cryptouser.h" +#include "tst_netlink.h" /** - * A reference to a crypto session and associated state. + * tst_crypto_add_alg() - Add a crypto algorithm to a session. * - * Holds state relevant to a netlink crypto connection. The seq_num is used - * to tag each message sent to the netlink layer and is automatically - * incremented by the tst_crypto_ functions. When the netlink layer sends a - * response (ack) it will use the sequences number from the request. - * - * Some functions, such as delete ALG, may return EBUSY in which case it is - * safe to retry them. The retries field allows you to set the number of - * times this should be done. If set to zero the operation will only be tried - * once. For operations which do not return EBUSY, the field is ignored. - * - * Use TST_CRYPTO_SESSION_INIT to statically initialize this struct with sane - * defaults. - */ -struct tst_crypto_session { - /** File descriptor for the netlink socket */ - int fd; - /** A sequence number used to identify responses from the kernel. */ - uint32_t seq_num; - /** Number of times some operations will be retried. */ - uint32_t retries; -}; - -/** - * Default static definition of tst_crypto_session. - * - * @relates tst_crypto_session - */ -#define TST_CRYPTO_SESSION_INIT {\ - .fd = 0, \ - .seq_num = 0, \ - .retries = 1000 \ -} - -/** - * Creates a crypto session. - * - * @relates tst_crypto_session - * @param ses Session structure to use, it can be uninitialized. - * - * If some necessary feature is missing then it will call tst_brk() with - * TCONF, for any other error it will use TBROK. - */ -void tst_crypto_open(struct tst_crypto_session *ses); - -/** - * Close a crypto session. - * - * @relates tst_crypto_session - * @param ses The session to close. - */ -void tst_crypto_close(struct tst_crypto_session *ses); - -/** - * Add a crypto algorithm to a session. - * - * @relates tst_crypto_session - * @param ses An open session. - * @param alg The crypto algorithm or module to add. + * @ctx: Initialized netlink context + * @alg: The crypto algorithm or module to add. * * This requests a new crypto algorithm/engine/module to be initialized by the * kernel. It sends the request contained in alg and then waits for a * response. If sending the message or receiving the ack fails at the netlink * level then tst_brk() with TBROK will be called. * - * @return On success it will return 0 otherwise it will return an inverted - * error code from the crypto layer. + * Return: On success it will return 0 otherwise it will return an inverted + * error code from the crypto layer. */ -int tst_crypto_add_alg(struct tst_crypto_session *ses, +int tst_crypto_add_alg(struct tst_netlink_context *ctx, const struct crypto_user_alg *alg); /** - * Delete a crypto algorithm from a session. + * tst_crypto_del_alg() - Delete a crypto algorithm from a session. * - * @relates tst_crypto_session - * @param ses An open session. - * @param alg The crypto algorithm to delete. + * @ctx: Initialized netlink context + * @alg: The crypto algorithm to delete. + * @retries: Number of retries before giving up. Recommended value: 1000 * * Request that the kernel remove an existing crypto algorithm. This behaves * in a similar way to tst_crypto_add_alg() except that it is the inverse @@ -102,11 +47,11 @@ int tst_crypto_add_alg(struct tst_crypto_session *ses, * EBUSY. * * Return: Either 0 or an inverted error code from the crypto layer. If called - * during cleanup it may return a positive ENODATA value from the LTP - * library, you don't need to log this error as it will already have - * been printed by tst_brk(). + * during cleanup it may return a positive ENODATA value from the LTP + * library, you don't need to log this error as it will already have + * been printed by tst_brk(). */ -int tst_crypto_del_alg(struct tst_crypto_session *ses, - const struct crypto_user_alg *alg); +int tst_crypto_del_alg(struct tst_netlink_context *ctx, + const struct crypto_user_alg *alg, unsigned int retries); #endif /* TST_CRYPTO_H */ diff --git a/include/tst_device.h b/include/tst_device.h index 36258f43..898335b1 100755 --- a/include/tst_device.h +++ b/include/tst_device.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2016-2019 Cyril Hrubis + * Copyright (c) Linux Test Project, 2019-2024 */ #ifndef TST_DEVICE_H__ @@ -14,6 +15,8 @@ struct tst_device { const char *dev; const char *fs_type; uint64_t size; + /* If device was mounted by the test library this flag is set for fuse fileystems. */ + int is_fuse; }; /* @@ -31,7 +34,10 @@ int tst_umount(const char *path); * Verifies if an earlier mount is successful or not. * @path: Mount path to verify */ +int tst_mount_has_opt(const char *path, const char *opt); int tst_is_mounted(const char *path); +int tst_is_mounted_ro(const char *path); +int tst_is_mounted_rw(const char *path); int tst_is_mounted_at_tmpdir(const char *path); /* @@ -66,7 +72,9 @@ int tst_attach_device(const char *dev_path, const char *file_path); uint64_t tst_get_device_size(const char *dev_path); /* - * Detaches a file from a loop device fd. + * Detaches a file from a loop device fd. @dev_fd needs to be the + * last descriptor opened. Call to this function will close it, + * it is up to caller to open it again for further usage. * * @dev_path Path to the loop device e.g. /dev/loop0 * @dev_fd a open fd for the loop device @@ -101,11 +109,6 @@ int tst_dev_sync(int fd); */ unsigned long tst_dev_bytes_written(const char *dev); -/* - * Wipe the contents of given directory but keep the directory itself - */ -void tst_purge_dir(const char *path); - /* * Find the file or path belongs to which block dev * @path Path to find the backing dev diff --git a/include/tst_fd.h b/include/tst_fd.h new file mode 100644 index 00000000..2183ea06 --- /dev/null +++ b/include/tst_fd.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2023 Cyril Hrubis + */ + +#ifndef TST_FD_H__ +#define TST_FD_H__ + +enum tst_fd_type { + TST_FD_FILE, + TST_FD_PATH, + TST_FD_DIR, + TST_FD_DEV_ZERO, + TST_FD_PROC_MAPS, + TST_FD_PIPE_READ, + TST_FD_PIPE_WRITE, + TST_FD_UNIX_SOCK, + TST_FD_INET_SOCK, + TST_FD_EPOLL, + TST_FD_EVENTFD, + TST_FD_SIGNALFD, + TST_FD_TIMERFD, + TST_FD_PIDFD, + TST_FD_FANOTIFY, + TST_FD_INOTIFY, + TST_FD_USERFAULTFD, + TST_FD_PERF_EVENT, + TST_FD_IO_URING, + TST_FD_BPF_MAP, + TST_FD_FSOPEN, + TST_FD_FSPICK, + TST_FD_OPEN_TREE, + TST_FD_MEMFD, + TST_FD_MEMFD_SECRET, + TST_FD_MAX, +}; + +struct tst_fd { + enum tst_fd_type type; + int fd; + /* used by the library, do not touch! */ + long priv; +}; + +#define TST_FD_INIT {.type = TST_FD_FILE, .fd = -1} + +/* + * Advances the iterator to the next fd type, returns zero at the end. + */ +int tst_fd_next(struct tst_fd *fd); + +#define TST_FD_FOREACH(fd) \ + for (struct tst_fd fd = TST_FD_INIT; tst_fd_next(&fd); ) + +/* + * Returns human readable name for the file descriptor type. + */ +const char *tst_fd_desc(struct tst_fd *fd); + +#endif /* TST_FD_H__ */ diff --git a/include/tst_fips.h b/include/tst_fips.h deleted file mode 100755 index 881c3239..00000000 --- a/include/tst_fips.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2021 Petr Vorel - */ - -#ifndef TST_FIPS_H__ -#define TST_FIPS_H__ - -/* - * Detect whether FIPS enabled - * @return 0: FIPS not enabled, 1: FIPS enabled - */ -int tst_fips_enabled(void); - -#endif /* TST_FIPS_H__ */ diff --git a/include/tst_fs.h b/include/tst_fs.h index 769fac1e..ceae78e7 100755 --- a/include/tst_fs.h +++ b/include/tst_fs.h @@ -6,7 +6,7 @@ #ifndef TST_FS_H__ #define TST_FS_H__ -/* man 2 statfs or kernel-source/include/linux/magic.h */ +/* man 2 statfs or kernel-source/include/uapi/linux/magic.h */ #define TST_BTRFS_MAGIC 0x9123683E #define TST_NFS_MAGIC 0x6969 #define TST_RAMFS_MAGIC 0x858458f6 @@ -34,6 +34,11 @@ #define TST_VFAT_MAGIC 0x4d44 /* AKA MSDOS */ #define TST_EXFAT_MAGIC 0x2011BAB0UL +/* fs/bcachefs/bcachefs_format.h */ +#define TST_BCACHE_MAGIC 0xca451a4e + +#include + enum tst_fill_access_pattern { TST_FILL_BLOCKS, TST_FILL_RANDOM @@ -57,7 +62,7 @@ enum { * @mult: mult should be TST_KB, TST_MB or TST_GB * the required free space is calculated by @size * @mult */ -int tst_fs_has_free_(void (*cleanup)(void), const char *path, unsigned int size, +int tst_fs_has_free_(void (*cleanup)(void), const char *path, uint64_t size, unsigned int mult); /* @@ -140,6 +145,16 @@ int tst_dir_is_empty_(void (*cleanup)(void), const char *name, int verbose); */ int tst_get_path(const char *prog_name, char *buf, size_t buf_len); +/** + * tst_path_exists() - checks if path exists + * + * @fmt: A printf-like format used to construct the path. + * @...: A printf-like parameter list. + * return: Non-zero if path exists, zero otherwise. + */ +int tst_path_exists(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + /* * Fill a file with specified pattern * @fd: file descriptor @@ -188,13 +203,6 @@ enum tst_fs_impl { */ enum tst_fs_impl tst_fs_is_supported(const char *fs_type); -/* - * Returns NULL-terminated array of kernel-supported filesystems. - * - * @skiplist A NULL terminated array of filesystems to skip. - */ -const char **tst_get_supported_fs_types(const char *const *skiplist); - /* * Returns 1 if filesystem is in skiplist 0 otherwise. * @@ -209,7 +217,10 @@ int tst_fs_in_skiplist(const char *fs_type, const char *const *skiplist); void tst_fill_fs(const char *path, int verbose, enum tst_fill_access_pattern pattern); /* - * test if FIBMAP ioctl is supported + * Check if FIBMAP ioctl is supported. + * Tests needs to set .needs_root = 1 in order to avoid EPERM. + * + * @return 0: FIBMAP is supported, 1: FIBMAP is *not* supported. */ int tst_fibmap(const char *filename); @@ -219,7 +230,7 @@ static inline long tst_fs_type(const char *path) return tst_fs_type_(NULL, path); } -static inline int tst_fs_has_free(const char *path, unsigned int size, +static inline int tst_fs_has_free(const char *path, uint64_t size, unsigned int mult) { return tst_fs_has_free_(NULL, path, size, mult); @@ -246,7 +257,7 @@ static inline long tst_fs_type(void (*cleanup)(void), const char *path) } static inline int tst_fs_has_free(void (*cleanup)(void), const char *path, - unsigned int size, unsigned int mult) + uint64_t size, unsigned int mult) { return tst_fs_has_free_(cleanup, path, size, mult); } diff --git a/include/tst_fuzzy_sync.h b/include/tst_fuzzy_sync.h index bef42400..b22364ca 100755 --- a/include/tst_fuzzy_sync.h +++ b/include/tst_fuzzy_sync.h @@ -155,11 +155,11 @@ struct tst_fzsync_pair { float max_dev_ratio; /** Internal; Atomic counter used by fzsync_pair_wait() */ - int a_cntr; + tst_atomic_t a_cntr; /** Internal; Atomic counter used by fzsync_pair_wait() */ - int b_cntr; + tst_atomic_t b_cntr; /** Internal; Used by tst_fzsync_pair_exit() and fzsync_pair_wait() */ - int exit; + tst_atomic_t exit; /** Internal; The test time remaining on tst_fzsync_pair_reset() */ float exec_time_start; /** diff --git a/include/tst_hugepage.h b/include/tst_hugepage.h index 46327c79..6b865b2f 100755 --- a/include/tst_hugepage.h +++ b/include/tst_hugepage.h @@ -24,6 +24,8 @@ enum tst_hp_policy { TST_NEEDS, }; +#define TST_NO_HUGEPAGES ((unsigned long)-1) + struct tst_hugepage { const unsigned long number; enum tst_hp_policy policy; diff --git a/include/tst_kconfig.h b/include/tst_kconfig.h index cc0908ea..b0608498 100755 --- a/include/tst_kconfig.h +++ b/include/tst_kconfig.h @@ -6,6 +6,17 @@ #ifndef TST_KCONFIG_H__ #define TST_KCONFIG_H__ +#include +#include + +/** + * Initialization helper macro for struct tst_kconfig_var. Requires + */ +#define TST_KCONFIG_INIT(confname) { \ + .id = confname, \ + .id_len = strlen(confname) \ +} + struct tst_kconfig_var { char id[64]; unsigned int id_len; @@ -19,7 +30,7 @@ struct tst_kconfig_var { * tst_kconfig_var structures. * * The path to the kernel config should be autodetected in most of the cases as - * the code looks for know locations. It can be explicitely set/overrided with + * the code looks for know locations. It can be explicitly set/overridden with * the KCONFIG_PATH environment variable as well. * * The caller has to initialize the tst_kconfig_var structure. The id has to be @@ -37,7 +48,7 @@ struct tst_kconfig_var { * In the case that match is set to 'v' the val pointer points to a newly * allocated string that holds the value. * - * @param vars An array of caller initalized tst_kconfig_var structures. + * @param vars An array of caller initialized tst_kconfig_var structures. * @param vars_len Length of the vars array. */ void tst_kconfig_read(struct tst_kconfig_var vars[], size_t vars_len); @@ -56,4 +67,48 @@ void tst_kconfig_read(struct tst_kconfig_var vars[], size_t vars_len); */ int tst_kconfig_check(const char *const kconfigs[]); +/** + * Macro to prepare a tst_kcmdline_var structure with a given parameter name. + * + * It initializes the .key field with the provided name, sets the .value field + * to an empty string, and marks the parameter as not found (.found = false). + * + * This macro is typically used to prepopulate an array with configuration + * parameters before processing the actual command line arguments. + */ +#define TST_KCMDLINE_INIT(paraname) { \ + .key = paraname, \ + .value = "", \ + .found = false \ +} + +/** + * Structure for storing command-line parameter key and its corresponding + * value, and a flag indicating whether the parameter was found. + */ +struct tst_kcmdline_var { + const char *key; + char value[256]; + bool found; +}; + +/** + * Parses command-line parameters from /proc/cmdline and stores them in params array. + * params: The array of tst_kcmdline_var structures to be filled with parsed key-value pairs. + * params_len: The length of the params array, indicating how many parameters to parse. + */ +void tst_kcmdline_parse(struct tst_kcmdline_var params[], size_t params_len); + +/* + * tst_has_slow_kconfig() - Check if any performance-degrading kernel configs are enabled. + * + * This function iterates over the list of slow kernel configuration options + * (`tst_slow_kconfigs`) and checks if any of them are enabled in the running kernel. + * + * Return: + * - 1 if at least one slow kernel config is enabled. + * - 0 if none of the slow kernel configs are enabled. + */ +int tst_has_slow_kconfig(void); + #endif /* TST_KCONFIG_H__ */ diff --git a/include/tst_kernel.h b/include/tst_kernel.h index 9d3a8d31..63ecb19a 100755 --- a/include/tst_kernel.h +++ b/include/tst_kernel.h @@ -1,33 +1,70 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright (c) 2017 Cyril Hrubis + * Copyright (c) Linux Test Project, 2018-2024 */ #ifndef TST_KERNEL_H__ #define TST_KERNEL_H__ -/* - * Returns 32 if we are running on 32bit kernel and 64 if on 64bit kernel. +#include + +/** + * tst_kernel_bits() - Detect if running on 32bit or 64bit kernel. + * + * Return: 32 if the test process is running on 32bit kernel and 64 if on 64bit + * kernel. */ int tst_kernel_bits(void); -/* - * Checks if the kernel module is built-in. +/** + * tst_is_compat_mode() - Detect if running in compat mode. * - * @param driver The name of the driver. - * @return Returns 0 if builtin driver - * -1 when driver is missing or config file not available. - * On Android *always* 0 (always expect the driver is available). + * Detect if the test is 32bit binary executed on a 64bit kernel, + * i.e. we are testing the kernel compat layer. + * + * Return: non-zero if the test process is running in compat mode. + */ +int tst_is_compat_mode(void); + +/** + * tst_abi_bits() - Detect if compiled for required kernel ABI. + * + * @abi: kernel ABI bits (32 or 64). + * + * Return: true if compiled for required ABI or false otherwise. + */ +bool tst_abi_bits(int abi); + +/** + * tst_check_builtin_driver() - Check if the kernel module is built-in. + * + * @driver: the name of the driver. + * + * Return: 0 if builtin driver or -1 when driver is missing or config file not + * available. On Android *always* 0 (always expect the driver is available). */ int tst_check_builtin_driver(const char *driver); -/* - * Checks support for the kernel module (both built-in and loadable). +/** + * tst_check_driver() - Check support for the kernel module. * - * @param driver The name of the driver. - * @return Returns 0 if the kernel has the driver, - * -1 when driver is missing or config file not available. - * On Android *always* 0 (always expect the driver is available). + * Check support for the kernel module (both built-in and loadable). + * + * @driver: the name of the driver. + * + * Return: 0 if the kernel has the driver, -1 when driver is missing or config + * file not available. On Android *always* 0 (always expect the driver is + * available). */ int tst_check_driver(const char *driver); +/** + * tst_check_preempt_rt() - Check if the running kernel is RT. + * + * Check support for the kernel module (both built-in and loadable). + * + * Return: -1 if the kernel is RT, 0 otherwise. + */ +int tst_check_preempt_rt(void); + #endif /* TST_KERNEL_H__ */ diff --git a/include/tst_lockdown.h b/include/tst_lockdown.h deleted file mode 100755 index 2bd1aa25..00000000 --- a/include/tst_lockdown.h +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#ifndef TST_LOCKDOWN_H -#define TST_LOCKDOWN_H - -int tst_secureboot_enabled(void); -int tst_lockdown_enabled(void); - -#endif /* TST_LOCKDOWN_H */ diff --git a/include/tst_memutils.h b/include/tst_memutils.h index 19b59343..57c90c4a 100755 --- a/include/tst_memutils.h +++ b/include/tst_memutils.h @@ -58,4 +58,17 @@ void tst_enable_oom_protection(pid_t pid); */ void tst_disable_oom_protection(pid_t pid); +#define TST_PRINT_MEMINFO() safe_print_file(__FILE__, __LINE__, "/proc/meminfo") + +/** + * tst_mapping_in_range() - Returns true if there is a mapping provided range. + * + * @low: A lower address inside of the processe address space. + * @high: A higher address inside of the processe address space. + * + * return: Returns true if there is a mapping between low and high addresses in + * the process address space. + */ +int tst_mapping_in_range(unsigned long low, unsigned long high); + #endif /* TST_MEMUTILS_H__ */ diff --git a/include/tst_module.h b/include/tst_module.h index 2654c5af..e55321d1 100755 --- a/include/tst_module.h +++ b/include/tst_module.h @@ -1,12 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * Copyright (c) Linux Test Project, 2016-2024 * Alexey Kodanev */ #ifndef TST_MODULE_H #define TST_MODULE_H +#include + void tst_module_exists_(void (cleanup_fn)(void), const char *mod_name, char **mod_path); @@ -30,4 +33,21 @@ static inline void tst_module_unload(const char *mod_name) tst_module_unload_(NULL, mod_name); } +bool tst_module_signature_enforced_(void); + +static inline bool tst_module_signature_enforced(void) +{ + return tst_module_signature_enforced_(); +} + +void tst_requires_module_signature_disabled_(void); + +static inline void tst_requires_module_signature_disabled(void) +{ + tst_requires_module_signature_disabled_(); +} + +void tst_modprobe(const char *mod_name, char *const argv[]); +void tst_module_reload(const char *mod_name, char *const argv[]); + #endif /* TST_MODULE_H */ diff --git a/include/tst_netdevice.h b/include/tst_netdevice.h index 5e62ba06..2394f9c7 100755 --- a/include/tst_netdevice.h +++ b/include/tst_netdevice.h @@ -5,7 +5,7 @@ #ifndef TST_NETDEVICE_H #define TST_NETDEVICE_H -#include "tst_rtnetlink.h" +#include "tst_netlink.h" /* Find device index for given network interface name. */ int tst_netdev_index_by_name(const char *file, const int lineno, @@ -132,7 +132,7 @@ int tst_netdev_remove_route_inet(const char *file, const int lineno, int tst_netdev_add_qdisc(const char *file, const int lineno, int strict, const char *ifname, unsigned int family, unsigned int parent, unsigned int handle, const char *qd_kind, - const struct tst_rtnl_attr_list *config); + const struct tst_netlink_attr_list *config); #define NETDEV_ADD_QDISC(ifname, family, parent, handle, qd_kind, config) \ tst_netdev_add_qdisc(__FILE__, __LINE__, 1, (ifname), (family), \ (parent), (handle), (qd_kind), (config)) @@ -154,7 +154,7 @@ int tst_netdev_remove_qdisc(const char *file, const int lineno, int strict, int tst_netdev_add_traffic_class(const char *file, const int lineno, int strict, const char *ifname, unsigned int parent, unsigned int handle, const char *qd_kind, - const struct tst_rtnl_attr_list *config); + const struct tst_netlink_attr_list *config); #define NETDEV_ADD_TRAFFIC_CLASS(ifname, parent, handle, qd_kind, config) \ tst_netdev_add_traffic_class(__FILE__, __LINE__, 1, (ifname), \ (parent), (handle), (qd_kind), (config)) @@ -173,12 +173,17 @@ int tst_netdev_remove_traffic_class(const char *file, const int lineno, int tst_netdev_add_traffic_filter(const char *file, const int lineno, int strict, const char *ifname, unsigned int parent, unsigned int handle, unsigned int protocol, unsigned int priority, - const char *f_kind, const struct tst_rtnl_attr_list *config); + const char *f_kind, const struct tst_netlink_attr_list *config); #define NETDEV_ADD_TRAFFIC_FILTER(ifname, parent, handle, protocol, priority, \ f_kind, config) \ tst_netdev_add_traffic_filter(__FILE__, __LINE__, 1, (ifname), \ (parent), (handle), (protocol), (priority), (f_kind), (config)) +#define NETDEV_ADD_TRAFFIC_FILTER_RET(ifname, parent, handle, protocol, \ + priority, f_kind, config) \ + tst_netdev_add_traffic_filter(__FILE__, __LINE__, 0, (ifname), \ + (parent), (handle), (protocol), (priority), (f_kind), (config)) + int tst_netdev_remove_traffic_filter(const char *file, const int lineno, int strict, const char *ifname, unsigned int parent, unsigned int handle, unsigned int protocol, unsigned int priority, diff --git a/include/tst_netlink.h b/include/tst_netlink.h index 2030ac30..7d96fd71 100755 --- a/include/tst_netlink.h +++ b/include/tst_netlink.h @@ -1,11 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright (c) 2018 Richard Palethorpe - */ - -/** - * @file tst_netlink.h - * - * Library for communicating with the kernel over the netlink interface. + * Copyright (c) 2021 Linux Test Project */ #ifndef TST_NETLINK_H @@ -13,76 +7,129 @@ #include -#ifndef NETLINK_CRYPTO -/** - * The netlink-crypto socket protocol. +struct tst_netlink_context; + +struct tst_netlink_attr_list { + unsigned short type; + const void *data; + ssize_t len; + const struct tst_netlink_attr_list *sublist; +}; + +struct tst_netlink_message { + struct nlmsghdr *header; + struct nlmsgerr *err; + void *payload; + size_t payload_size; +}; + +extern int tst_netlink_errno; + +/* Open a netlink socket */ +struct tst_netlink_context *tst_netlink_create_context(const char *file, + const int lineno, int protocol); +#define NETLINK_CREATE_CONTEXT(protocol) \ + tst_netlink_create_context(__FILE__, __LINE__, (protocol)) + +/* Free a tst_netlink_message array returned by tst_netlink_recv() */ +void tst_netlink_free_message(struct tst_netlink_message *msg); +#define NETLINK_FREE_MESSAGE tst_netlink_free_message + +/* Close netlink socket */ +void tst_netlink_destroy_context(const char *file, const int lineno, + struct tst_netlink_context *ctx); +#define NETLINK_DESTROY_CONTEXT(ctx) \ + tst_netlink_destroy_context(__FILE__, __LINE__, (ctx)) + +/* Send all messages in given buffer */ +int tst_netlink_send(const char *file, const int lineno, + struct tst_netlink_context *ctx); +#define NETLINK_SEND(ctx) tst_netlink_send(__FILE__, __LINE__, (ctx)) + +/* Send all messages in given buffer and validate kernel response */ +int tst_netlink_send_validate(const char *file, const int lineno, + struct tst_netlink_context *ctx); +#define NETLINK_SEND_VALIDATE(ctx) \ + tst_netlink_send_validate(__FILE__, __LINE__, (ctx)) + +/* Wait until data is available for reading from the netlink socket */ +int tst_netlink_wait(struct tst_netlink_context *ctx); +#define NETLINK_WAIT tst_netlink_wait + +/* + * Read from netlink socket and return an array of partially parsed messages. + * header == NULL indicates end of array. */ -#define NETLINK_CRYPTO 21 -#endif +struct tst_netlink_message *tst_netlink_recv(const char *file, const int lineno, + struct tst_netlink_context *ctx); +#define NETLINK_RECV(ctx) tst_netlink_recv(__FILE__, __LINE__, (ctx)) -/** @private */ -static inline ssize_t safe_netlink_send(const char *file, const int lineno, - int fd, const struct nlmsghdr *nh, - const void *payload) -{ - struct sockaddr_nl sa = { .nl_family = AF_NETLINK }; - struct iovec iov[2] = { - {(struct nlmsghdr *)nh, sizeof(*nh)}, - {(void *)payload, nh->nlmsg_len - sizeof(*nh)} - }; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = iov, - .msg_iovlen = 2 - }; +/* Add new message to buffer */ +int tst_netlink_add_message(const char *file, const int lineno, + struct tst_netlink_context *ctx, const struct nlmsghdr *header, + const void *payload, size_t payload_size); +#define NETLINK_ADD_MESSAGE(ctx, header, payload, psize) \ + tst_netlink_add_message(__FILE__, __LINE__, (ctx), (header), \ + (payload), (psize)) - return safe_sendmsg(file, lineno, nh->nlmsg_len, fd, &msg, 0); -} +/* Add arbitrary nlattr attribute to last message */ +int tst_netlink_add_attr(const char *file, const int lineno, + struct tst_netlink_context *ctx, unsigned short type, const void *data, + unsigned short len); +#define NETLINK_ADD_ATTR(ctx, type, data, len) \ + tst_netlink_add_attr(__FILE__, __LINE__, (ctx), (type), (data), (len)) -/** - * Sends a netlink message using safe_sendmsg(). - * - * @param fd netlink socket file descriptor. - * @param nl_header netlink header structure describing the message. - * @param payload an opaque object containing the message data. - * - * You should set the message length, type and flags to appropriate values - * within the nl_header object. See lib/tst_crypto.c for an example. - * - * @return The number of bytes sent. +/* Add string nlattr attribute to last message */ +int tst_netlink_add_attr_string(const char *file, const int lineno, + struct tst_netlink_context *ctx, unsigned short type, const char *data); +#define NETLINK_ADD_ATTR_STRING(ctx, type, data) \ + tst_netlink_add_attr_string(__FILE__, __LINE__, (ctx), (type), (data)) + +/* + * Add list of arbitrary nlattr attributes to last message. The list is + * terminated by attribute with negative length. Nested sublists are supported. */ -#define SAFE_NETLINK_SEND(fd, nl_header, payload) \ - safe_netlink_send(__FILE__, __LINE__, fd, nl_header, payload) +int tst_netlink_add_attr_list(const char *file, const int lineno, + struct tst_netlink_context *ctx, + const struct tst_netlink_attr_list *list); +#define NETLINK_ADD_ATTR_LIST(ctx, list) \ + tst_netlink_add_attr_list(__FILE__, __LINE__, (ctx), (list)) -/** @private */ -static inline ssize_t safe_netlink_recv(const char *file, const int lineno, - int fd, char *nl_headers_buf, - size_t buf_len) -{ - struct iovec iov = { nl_headers_buf, buf_len }; - struct sockaddr_nl sa; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = &iov, - .msg_iovlen = 1 - }; +/* Add arbitrary rtattr attribute to last message */ +int tst_rtnl_add_attr(const char *file, const int lineno, + struct tst_netlink_context *ctx, unsigned short type, const void *data, + unsigned short len); +#define RTNL_ADD_ATTR(ctx, type, data, len) \ + tst_rtnl_add_attr(__FILE__, __LINE__, (ctx), (type), (data), (len)) - return safe_recvmsg(file, lineno, 0, fd, &msg, 0); -} +/* Add string rtattr attribute to last message */ +int tst_rtnl_add_attr_string(const char *file, const int lineno, + struct tst_netlink_context *ctx, unsigned short type, const char *data); +#define RTNL_ADD_ATTR_STRING(ctx, type, data) \ + tst_rtnl_add_attr_string(__FILE__, __LINE__, (ctx), (type), (data)) -/** - * Receives a netlink message using safe_recvmsg(). - * - * @param fd netlink socket file descriptor. - * @param nl_header_buf buffer to contain the received netlink header structure. - * @param buf_len The length of the header buffer. Must be greater than the page - * size. - * - * @return The number of bytes received. +/* + * Add list of arbitrary rtattr attributes to last message. The list is + * terminated by attribute with negative length. Nested sublists are supported. */ -#define SAFE_NETLINK_RECV(fd, nl_header_buf, buf_len) \ - safe_netlink_recv(__FILE__, __LINE__, fd, nl_header_buf, buf_len) +int tst_rtnl_add_attr_list(const char *file, const int lineno, + struct tst_netlink_context *ctx, + const struct tst_netlink_attr_list *list); +#define RTNL_ADD_ATTR_LIST(ctx, list) \ + tst_rtnl_add_attr_list(__FILE__, __LINE__, (ctx), (list)) + +/* Check that all sent messages with NLM_F_ACK flag have been acked without + * error. Usage: + * + * tst_netlink_send(ctx); + * tst_netlink_wait(ctx); + * response = tst_netlink_recv(ctx); + * if (!tst_netlink_check_acks(ctx, response)) { ... } + * tst_netlink_free_message(response); + */ +int tst_netlink_check_acks(const char *file, const int lineno, + struct tst_netlink_context *ctx, struct tst_netlink_message *response); +#define NETLINK_CHECK_ACKS(ctx, response) \ + tst_netlink_check_acks(__FILE__, __LINE__, (ctx), (response)) #endif /* TST_NETLINK_H */ diff --git a/include/tst_numa.h b/include/tst_numa.h index 3af311e5..a1f96163 100755 --- a/include/tst_numa.h +++ b/include/tst_numa.h @@ -8,62 +8,67 @@ #include /** - * Numa nodemap. + * struct tst_nodemap - Numa nodemap. + * + * @cnt: Number of nodes in map. + * @counters: Page allocation counters. + * @map: Array of numa ids. */ struct tst_nodemap { - /** Number of nodes in map */ unsigned int cnt; - /** Page allocation counters */ unsigned int *counters; - /** Array of numa ids */ unsigned int map[]; }; /** - * Clears numa counters. The counters are lazy-allocated on first call of this function. + * tst_nodemap_reset_counters() - Clears numa counters. The counters are lazy-allocated on first call of this function. * - * @nodes Numa nodemap. + * @nodes: Numa nodemap. */ void tst_nodemap_reset_counters(struct tst_nodemap *nodes); /** - * Prints pages allocated per each node. + * tst_nodemap_print_counters() - Prints pages allocated per each node. * - * @nodes Numa nodemap. + * @nodes: Numa nodemap. */ void tst_nodemap_print_counters(struct tst_nodemap *nodes); /** - * Returns a name for a mempolicy/mbind mode. + * tst_mempolicy_mode_name() - Returns a name for a mempolicy/mbind mode. * - * @mode Numa mempolicy mode. + * @mode: Numa mempolicy mode. + * + * return: a name for a mempolicy/mbind mode. */ const char *tst_mempolicy_mode_name(int mode); /** - * Maps pages into memory, if path is NULL the mapping is anonymous otherwise is backed by the file. + * tst_numa_map() - Maps pages into memory, if path is NULL the mapping is anonymous otherwise is backed by the file. * - * @path Path to a file, if not NULL mapping is file based. - * @size Mapping size. + * @path: Path to a file, if not NULL mapping is file based. + * @size: Mapping size. + * + * return: a pointer to a mapped file. */ void *tst_numa_map(const char *path, size_t size); -/* - * Writes to memory in order to get the pages faulted. +/** + * tst_numa_fault() - Writes to memory in order to get the pages faulted. * - * @ptr Start of the mapping. - * @size Size of the mapping. + * @ptr: Start of the mapping. + * @size: Size of the mapping. */ static inline void tst_numa_fault(void *ptr, size_t size) { memset(ptr, 'a', size); } -/* - * Frees the memory. +/** + * tst_numa_unmap() - Frees the memory. * - * @ptr Start of the mapping. - * @size Size of the mapping. + * @ptr: Start of the mapping. + * @size: Size of the mapping. */ static inline void tst_numa_unmap(void *ptr, size_t size) { @@ -71,24 +76,29 @@ static inline void tst_numa_unmap(void *ptr, size_t size) } /** + * tst_nodemap_count_pages() - Check which numa node resides each page. + * * Check on which numa node resides each page of the mapping starting at ptr * and continuing pages long and increases nodemap counters accordingly. * - * @nodes Nodemap with initialized counters. - * @ptr Pointer to start of a mapping. - * @size Size of the mapping. + * @nodes: Nodemap with initialized counters. + * @ptr: Pointer to start of a mapping. + * @size: Size of the mapping. */ void tst_nodemap_count_pages(struct tst_nodemap *nodes, void *ptr, size_t size); /** - * Frees nodemap. + * tst_nodemap_free() - Frees nodemap. * - * @nodes Numa nodemap to be freed. + * @nodes: Numa nodemap to be freed. */ void tst_nodemap_free(struct tst_nodemap *nodes); /** - * Bitflags for tst_get_nodemap() function. + * enum tst_numa_types - Bitflags for tst_get_nodemap() function. + * + * @TST_NUMA_ANY: general NUMA node. + * @TST_NUMA_MEM: NUMA memory node. */ enum tst_numa_types { TST_NUMA_ANY = 0x00, @@ -96,15 +106,15 @@ enum tst_numa_types { }; /** - * Allocates and returns numa node map, which is an array of numa nodes which + * tst_get_nodemap() - Allocates and returns numa node map, which is an array of numa nodes which * contain desired resources e.g. memory. * - * @type Bitflags of enum tst_numa_types specifying desired resources. - * @min_mem_kb Minimal free RAM on memory nodes, if given node has less than + * @type: Bitflags of enum tst_numa_types specifying desired resources. + * @min_mem_kb: Minimal free RAM on memory nodes, if given node has less than * requested amount of free+buffers memory it's not included in * the resulting list of nodes. * - * @return On success returns allocated and initialized struct tst_nodemap which contains + * return: On success returns allocated and initialized struct tst_nodemap which contains * array of numa node ids that contains desired resources. */ struct tst_nodemap *tst_get_nodemap(int type, size_t min_mem_kb); diff --git a/include/tst_parse.h b/include/tst_parse.h new file mode 100644 index 00000000..167d416f --- /dev/null +++ b/include/tst_parse.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2015-2024 Cyril Hrubis + */ + +/** + * DOC: Option parsing functions + * + * Implements simple helpers on the top of the strtol() and strtod() for + * command line option parsing. + */ + +#ifndef TST_PARSE_H__ +#define TST_PARSE_H__ + +/** + * tst_parse_int() - Parse an integer from a string. + * + * @str: A string with an integer number. + * @val: A pointer to integer to store the result to. + * @min: A lower bound, pass INT_MIN for full range. + * @max: An upper bound, pass INT_MAX for full range. + * return: A zero if whole string was consumed and the value was within bounds, + * an errno otherwise. + */ +int tst_parse_int(const char *str, int *val, int min, int max); + +/** + * tst_parse_long() - Parse a long integer from a string. + * + * @str: A string with an integer number. + * @val: A pointer to long integer to store the result to. + * @min: A lower bound, pass LONG_MIN for full range. + * @max: An upper bound, pass LONG_MAX for full range. + * return: A zero if whole string was consumed and the value was within bounds, + * an errno otherwise. + */ +int tst_parse_long(const char *str, long *val, long min, long max); + +/** + * tst_parse_float() - Parse a floating point number from a string. + * + * @str: A string with a floating point number. + * @val: A pointer to float to store the result to. + * @min: A lower bound. + * @max: An upper bound. + * return: A zero if whole string was consumed and the value was within bounds, + * an errno otherwise. + */ +int tst_parse_float(const char *str, float *val, float min, float max); + +/** + * tst_parse_filesize() - Parse a file size from a string. + * + * @str: A string a positive number optionally followed by an unit, i.e. K, M, + * or G for kilobytes, megabytes and gigabytes. + * @val: A pointer to long long integer to store the size in bytes to. + * @min: A lower bound. + * @max: An upper bound. + * return: A zero if whole string was consumed and the value was within bounds, + * an errno otherwise. + */ +int tst_parse_filesize(const char *str, long long *val, long long min, long long max); + +#endif /* TST_PARSE_H__ */ diff --git a/include/tst_pid.h b/include/tst_pid.h index 774c845c..951138ab 100755 --- a/include/tst_pid.h +++ b/include/tst_pid.h @@ -50,4 +50,12 @@ static inline int tst_get_free_pids(void (*cleanup_fn)(void)) */ pid_t tst_getpid(void); +/* + * Direct gettid() syscall. Some glibc versions cache gettid() return value + * which can cause confusing issues for example in processes created by + * direct clone() syscall (without using the glibc wrapper). Use this function + * whenever the current process may be a thread of the main test process. + */ +pid_t tst_gettid(void); + #endif /* TST_PID_H__ */ diff --git a/include/tst_private.h b/include/tst_private.h index 6f4f39b1..292f7e93 100755 --- a/include/tst_private.h +++ b/include/tst_private.h @@ -40,11 +40,18 @@ char tst_kconfig_get(const char *confname); /* * If cmd argument is a single command, this function just checks command - * whether exists. If not, case skips. + * whether exists. If not, case breaks if brk_nosupp is defined. * If cmd argument is a complex string ie 'mkfs.ext4 >= 1.43.0', this * function checks command version whether meets this requirement. - * If not, case skips. + * If not, case breaks if brk_nosupp is defined. */ -void tst_check_cmd(const char *cmd); +int tst_check_cmd(const char *cmd, const int brk_nosupp); + +/* + * Returns NULL-terminated array of kernel-supported filesystems. + * + * @skiplist A NULL terminated array of filesystems to skip. + */ +const char **tst_get_supported_fs_types(const char *const *skiplist); #endif diff --git a/include/tst_res_flags.h b/include/tst_res_flags.h index 8eda2f8b..eb291b6b 100755 --- a/include/tst_res_flags.h +++ b/include/tst_res_flags.h @@ -1,23 +1,81 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* * Copyright (c) Linux Test Project, 2014 */ #ifndef TST_RES_FLAGS_H #define TST_RES_FLAGS_H -/* Use low 6 bits to encode test type */ -#define TTYPE_MASK 0x3f -#define TPASS 0 /* Test passed flag */ -#define TFAIL 1 /* Test failed flag */ -#define TBROK 2 /* Test broken flag */ -#define TWARN 4 /* Test warning flag */ -#define TINFO 16 /* Test information flag */ -#define TCONF 32 /* Test not appropriate for configuration flag */ -#define TTYPE_RESULT(ttype) ((ttype) & TTYPE_MASK) +/** + * enum tst_res_flags - Test result reporting flags. + * + * @TPASS: Reports a single success. Successes increment passed counter and + * show up in the test results. + * + * @TFAIL: Reports a single failure. Failures increment failure counter and + * show up in the test results. A failure occurs when test assertion + * is broken. + * + * @TBROK: Reports a single breakage. Breakages increment breakage counter and + * show up in the test results. Breakages are reported in cases where a + * test couldn't be executed due to an unexpected failure during the + * test setup. The TBROK status is mostly used with tst_brk() which + * exit the test immediately. The difference between TBROK and TCONF is + * that TCONF is used in cases where optional functionality is missing + * while TBROK is used in cases where something that is supposed to + * work is broken unexpectedly. + * + * @TWARN: Reports a single warning. Warnings increment a warning counter and + * show up in test results. Warnings are somewhere in the middle between + * TBROK and TCONF. Warnings usually appear when something that is + * supposed to be working is broken but the test can somehow continue. + * + * @TDEBUG: Prints additional debugging messages, it does not change the test result counters and + * the message is not displayed unless debugging is enabled with -D + * test command line parameter. + * + * @TINFO: Prints an additional information, it does not change the test result + * counters but unlike TDEBUG the message is always displayed. + * + * @TCONF: Reports unsupported configuration. When tests produce this result at + * least a subset of test was skipped, because it couldn't run. The + * usual reasons are, missing kernel modules or CONFIG options. + * Unsuitable CPU architecture, not enough memory, etc. + * + * @TERRNO: Combine bitwise with result flags to append errno to the output message. + * + * @TTERRNO: Combine bitwise with result flags to append error from TST_ERR to + * the message. The TST_TEST() macros store the errno into the + * TST_ERR global variable in order to make sure it's not change + * between the test is done and results are printed. + * + * @TRERRNO: Combine bitwise with result flags to errno from TST_RET variable + * to the message. The TST_TEST() macros store return value into the + * TST_RET global variable and quite a few, e.g. pthread functions, + * return the error value directly instead of storing it to the errno. + * + * A result flag with optional bitwise combination of errno flag are passed to + * the tst_res() and tst_brk() functions. Each message counts as a single test + * result and tests can produce arbitrary number of results, i.e. TPASS, TFAIL, + * TBROK, TWARN and TCONF messages. Each such message increases a result + * counter in a piece of shared memory, which means that reported results are + * accounted immediately even from child processes and there is no need for + * result propagation. + */ +enum tst_res_flags { + TPASS = 0, + TFAIL = 1, + TBROK = 2, + TWARN = 4, + TDEBUG = 8, + TINFO = 16, + TCONF = 32, + TERRNO = 0x100, + TTERRNO = 0x200, + TRERRNO = 0x400, +}; -#define TERRNO 0x100 /* Append errno information to output */ -#define TTERRNO 0x200 /* Append TEST_ERRNO information to output */ -#define TRERRNO 0x400 /* Capture errno information from TEST_RETURN to - output; useful for pthread-like APIs :). */ +#define TTYPE_RESULT(ttype) ((ttype) & TTYPE_MASK) +#define TTYPE_MASK 0x3f #endif /* TST_RES_FLAGS_H */ diff --git a/include/tst_rtnetlink.h b/include/tst_rtnetlink.h deleted file mode 100755 index 6a0c53df..00000000 --- a/include/tst_rtnetlink.h +++ /dev/null @@ -1,108 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright (c) 2021 Linux Test Project - */ - -#ifndef TST_RTNETLINK_H -#define TST_RTNETLINK_H - -struct tst_rtnl_context; - -struct tst_rtnl_attr_list { - unsigned short type; - const void *data; - ssize_t len; - const struct tst_rtnl_attr_list *sublist; -}; - -struct tst_rtnl_message { - struct nlmsghdr *header; - struct nlmsgerr *err; - void *payload; - size_t payload_size; -}; - -extern int tst_rtnl_errno; - -/* Open a netlink socket */ -struct tst_rtnl_context *tst_rtnl_create_context(const char *file, - const int lineno); -#define RTNL_CREATE_CONTEXT() tst_rtnl_create_context(__FILE__, __LINE__) - -/* Free a tst_rtnl_message array returned by tst_rtnl_recv() */ -void tst_rtnl_free_message(struct tst_rtnl_message *msg); -#define RTNL_FREE_MESSAGE tst_rtnl_free_message - -/* Close netlink socket */ -void tst_rtnl_destroy_context(const char *file, const int lineno, - struct tst_rtnl_context *ctx); -#define RTNL_DESTROY_CONTEXT(ctx) \ - tst_rtnl_destroy_context(__FILE__, __LINE__, (ctx)) - -/* Send all messages in given buffer */ -int tst_rtnl_send(const char *file, const int lineno, - struct tst_rtnl_context *ctx); -#define RTNL_SEND(ctx) tst_rtnl_send(__FILE__, __LINE__, (ctx)) - -/* Send all messages in given buffer and validate kernel response */ -int tst_rtnl_send_validate(const char *file, const int lineno, - struct tst_rtnl_context *ctx); -#define RTNL_SEND_VALIDATE(ctx) \ - tst_rtnl_send_validate(__FILE__, __LINE__, (ctx)) - -/* Wait until data is available for reading from the netlink socket */ -int tst_rtnl_wait(struct tst_rtnl_context *ctx); -#define RTNL_WAIT tst_rtnl_wait - -/* - * Read from netlink socket and return an array of partially parsed messages. - * header == NULL indicates end of array. - */ -struct tst_rtnl_message *tst_rtnl_recv(const char *file, const int lineno, - struct tst_rtnl_context *ctx); -#define RTNL_RECV(ctx) tst_rtnl_recv(__FILE__, __LINE__, (ctx)) - -/* Add new message to buffer */ -int tst_rtnl_add_message(const char *file, const int lineno, - struct tst_rtnl_context *ctx, const struct nlmsghdr *header, - const void *payload, size_t payload_size); -#define RTNL_ADD_MESSAGE(ctx, header, payload, psize) \ - tst_rtnl_add_message(__FILE__, __LINE__, (ctx), (header), (payload), \ - (psize)) - -/* Add arbitrary attribute to last message */ -int tst_rtnl_add_attr(const char *file, const int lineno, - struct tst_rtnl_context *ctx, unsigned short type, const void *data, - unsigned short len); -#define RTNL_ADD_ATTR(ctx, type, data, len) \ - tst_rtnl_add_attr(__FILE__, __LINE__, (ctx), (type), (data), (len)) - -/* Add string attribute to last message */ -int tst_rtnl_add_attr_string(const char *file, const int lineno, - struct tst_rtnl_context *ctx, unsigned short type, const char *data); -#define RTNL_ADD_ATTR_STRING(ctx, type, data) \ - tst_rtnl_add_attr_string(__FILE__, __LINE__, (ctx), (type), (data)) - -/* - * Add list of arbitrary attributes to last message. The list is terminated - * by attribute with negative length. Nested sublists are supported. - */ -int tst_rtnl_add_attr_list(const char *file, const int lineno, - struct tst_rtnl_context *ctx, const struct tst_rtnl_attr_list *list); -#define RTNL_ADD_ATTR_LIST(ctx, list) \ - tst_rtnl_add_attr_list(__FILE__, __LINE__, (ctx), (list)) - -/* Check that all sent messages with NLM_F_ACK flag have been acked without - * error. Usage: - * - * tst_rtnl_send(ctx); - * tst_rtnl_wait(ctx); - * response = tst_rtnl_recv(ctx); - * if (!tst_rtnl_check_acks(ctx, response)) { ... } - * tst_rtnl_free_message(response); - */ -int tst_rtnl_check_acks(const char *file, const int lineno, - struct tst_rtnl_context *ctx, struct tst_rtnl_message *response); -#define RTNL_CHECK_ACKS(ctx, response) \ - tst_rtnl_context(__FILE__, __LINE__, (ctx), (response)) - -#endif /* TST_RTNETLINK_H */ diff --git a/include/tst_safe_clocks.h b/include/tst_safe_clocks.h index 5661ce57..5b0e8c5b 100755 --- a/include/tst_safe_clocks.h +++ b/include/tst_safe_clocks.h @@ -73,6 +73,26 @@ static inline int safe_clock_settime(const char *file, const int lineno, return rval; } +static inline int safe_clock_nanosleep(const char *file, const int lineno, + clockid_t clockid, int flags, const struct timespec *ts, + struct timespec *remain) +{ + int ret; + + errno = 0; + ret = clock_nanosleep(clockid, flags, ts, remain); + + if (ret == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "clock_nanosleep() failed"); + } else if (ret) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid clock_nanosleep() return value %d", ret); + } + + return ret; +} + static inline int safe_timer_create(const char *file, const int lineno, clockid_t clockid, struct sigevent *sevp, timer_t *timerid) { @@ -159,6 +179,9 @@ static inline int safe_timer_delete(const char *file, const int lineno, #define SAFE_CLOCK_SETTIME(clk_id, tp)\ safe_clock_settime(__FILE__, __LINE__, (clk_id), (tp)) +#define SAFE_CLOCK_NANOSLEEP(clockid, flags, ts, remain)\ + safe_clock_nanosleep(__FILE__, __LINE__, clockid, flags, ts, remain) + #define SAFE_TIMER_CREATE(clockid, sevp, timerid)\ safe_timer_create(__FILE__, __LINE__, (clockid), (sevp), (timerid)) diff --git a/include/tst_safe_file_ops.h b/include/tst_safe_file_ops.h index 62f6600e..0d881959 100755 --- a/include/tst_safe_file_ops.h +++ b/include/tst_safe_file_ops.h @@ -56,10 +56,16 @@ safe_touch(__FILE__, __LINE__, NULL, \ (pathname), (mode), (times)) +/* New API only functions */ + +/* helper functions to setup overlayfs mountpoint */ +void tst_create_overlay_dirs(void); +int tst_mount_overlay(const char *file, const int lineno, int strict); + #define SAFE_MOUNT_OVERLAY() \ - ((void) mount_overlay(__FILE__, __LINE__, 1)) + ((void) tst_mount_overlay(__FILE__, __LINE__, 1)) #define TST_MOUNT_OVERLAY() \ - (mount_overlay(__FILE__, __LINE__, 0) == 0) + (tst_mount_overlay(__FILE__, __LINE__, 0) == 0) #endif /* TST_SAFE_FILE_OPS */ diff --git a/include/tst_safe_macros.h b/include/tst_safe_macros.h index 0cf3d787..c73c1acb 100755 --- a/include/tst_safe_macros.h +++ b/include/tst_safe_macros.h @@ -1,18 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright (c) 2010-2018 Linux Test Project + * Copyright (c) 2010-2024 Linux Test Project * Copyright (c) 2011-2015 Cyril Hrubis */ #ifndef TST_SAFE_MACROS_H__ #define TST_SAFE_MACROS_H__ +#include #include #include #include #include #include +#include #include #include +#include #include #include #include @@ -21,8 +24,10 @@ #include #include +#include "safe_stdio_fn.h" #include "safe_macros_fn.h" #include "tst_cmd.h" +#include "tst_safe_macros_inline.h" int safe_access(const char *filename, const int lineno, const char *pathname, int mode); @@ -72,6 +77,11 @@ int safe_dup2(const char *file, const int lineno, int oldfd, int newfd); #define SAFE_MALLOC(size) \ safe_malloc(__FILE__, __LINE__, NULL, (size)) +void *safe_calloc(const char *file, const int lineno, size_t nmemb, size_t size); + +#define SAFE_CALLOC(nmemb, size) \ + safe_calloc(__FILE__, __LINE__, (nmemb), (size)) + void *safe_realloc(const char *file, const int lineno, void *ptr, size_t size); #define SAFE_REALLOC(ptr, size) \ @@ -86,6 +96,12 @@ void *safe_realloc(const char *file, const int lineno, void *ptr, size_t size); #define SAFE_MUNMAP(addr, length) \ safe_munmap(__FILE__, __LINE__, NULL, (addr), (length)) +int safe_msync(const char *file, const int lineno, void *addr, + size_t length, int flags); + +#define SAFE_MSYNC(addr, length, flags) \ + safe_msync(__FILE__, __LINE__, (addr), (length), (flags)) + #define SAFE_OPEN(pathname, oflags, ...) \ safe_open(__FILE__, __LINE__, NULL, (pathname), (oflags), \ ##__VA_ARGS__) @@ -204,6 +220,9 @@ int safe_getgroups(const char *file, const int lineno, int size, gid_t list[]); #define SAFE_FCHOWN(fd, owner, group) \ safe_fchown(__FILE__, __LINE__, NULL, (fd), (owner), (group)) +#define SAFE_LCHOWN(path, owner, group) \ + safe_lchown(__FILE__, __LINE__, NULL, (path), (owner), (group)) + #define SAFE_WAIT(status) \ safe_wait(__FILE__, __LINE__, NULL, (status)) @@ -225,7 +244,12 @@ int safe_getgroups(const char *file, const int lineno, int size, gid_t list[]); #define SAFE_MOUNT(source, target, filesystemtype, \ mountflags, data) \ safe_mount(__FILE__, __LINE__, NULL, (source), (target), \ - (filesystemtype), (mountflags), (data)) + (filesystemtype), (mountflags), (data), NULL) + +#define SAFE_MOUNT2(source, target, filesystemtype, \ + mountflags, data, is_fuse) \ + safe_mount(__FILE__, __LINE__, NULL, (source), (target), \ + (filesystemtype), (mountflags), (data), (is_fuse)) #define SAFE_UMOUNT(target) \ safe_umount(__FILE__, __LINE__, NULL, (target)) @@ -256,234 +280,11 @@ int safe_getgroups(const char *file, const int lineno, int size, gid_t list[]); "fcntl(%i,%s,...) failed", fd, #cmd), 0 \ : tst_ret_;}) -/* - * following functions are inline because the behaviour may depend on - * -D_FILE_OFFSET_BITS=64 compile flag - */ +int safe_mprotect(const char *file, const int lineno, + char *addr, size_t len, int prot); -static inline void *safe_mmap(const char *file, const int lineno, - void *addr, size_t length, - int prot, int flags, int fd, off_t offset) -{ - void *rval; - - rval = mmap(addr, length, prot, flags, fd, offset); - if (rval == MAP_FAILED) { - tst_brk_(file, lineno, TBROK | TERRNO, - "mmap(%p,%zu,%d,%d,%d,%ld) failed", - addr, length, prot, flags, fd, (long) offset); - } - - return rval; -} -#define SAFE_MMAP(addr, length, prot, flags, fd, offset) \ - safe_mmap(__FILE__, __LINE__, (addr), (length), (prot), \ - (flags), (fd), (offset)) - -static inline int safe_ftruncate(const char *file, const int lineno, - int fd, off_t length) -{ - int rval; - - rval = ftruncate(fd, length); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "ftruncate(%d,%ld) failed", fd, (long)length); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid ftruncate(%d,%ld) return value %d", fd, - (long)length, rval); - } - - return rval; -} -#define SAFE_FTRUNCATE(fd, length) \ - safe_ftruncate(__FILE__, __LINE__, (fd), (length)) - -static inline int safe_posix_fadvise(const char *file, const int lineno, - int fd, off_t offset, off_t len, int advice) -{ - int rval; - - rval = posix_fadvise(fd, offset, len, advice); - - if (rval) - tst_brk_(file, lineno, TBROK, - "posix_fadvise(%d,%ld,%ld,%d) failed: %s", - fd, (long)offset, (long)len, advice, tst_strerrno(rval)); - - return rval; -} -#define SAFE_POSIX_FADVISE(fd, offset, len, advice) \ - safe_posix_fadvise(__FILE__, __LINE__, (fd), (offset), (len), (advice)) - -static inline int safe_truncate(const char *file, const int lineno, - const char *path, off_t length) -{ - int rval; - - rval = truncate(path, length); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "truncate(%s,%ld) failed", path, (long)length); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid truncate(%s,%ld) return value %d", path, - (long)length, rval); - } - - return rval; -} -#define SAFE_TRUNCATE(path, length) \ - safe_truncate(__FILE__, __LINE__, (path), (length)) - -static inline int safe_stat(const char *file, const int lineno, - const char *path, struct stat *buf) -{ - int rval; - - rval = stat(path, buf); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "stat(%s,%p) failed", path, buf); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid stat(%s,%p) return value %d", path, buf, - rval); - } - - return rval; -} -#define SAFE_STAT(path, buf) \ - safe_stat(__FILE__, __LINE__, (path), (buf)) - -static inline int safe_fstat(const char *file, const int lineno, - int fd, struct stat *buf) -{ - int rval; - - rval = fstat(fd, buf); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "fstat(%d,%p) failed", fd, buf); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid fstat(%d,%p) return value %d", fd, buf, rval); - } - - return rval; -} -#define SAFE_FSTAT(fd, buf) \ - safe_fstat(__FILE__, __LINE__, (fd), (buf)) - -static inline int safe_lstat(const char *file, const int lineno, - const char *path, struct stat *buf) -{ - int rval; - - rval = lstat(path, buf); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "lstat(%s,%p) failed", path, buf); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid lstat(%s,%p) return value %d", path, buf, - rval); - } - - return rval; -} -#define SAFE_LSTAT(path, buf) \ - safe_lstat(__FILE__, __LINE__, (path), (buf)) - -static inline int safe_statfs(const char *file, const int lineno, - const char *path, struct statfs *buf) -{ - int rval; - - rval = statfs(path, buf); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "statfs(%s,%p) failed", path, buf); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid statfs(%s,%p) return value %d", path, buf, - rval); - } - - return rval; -} -#define SAFE_STATFS(path, buf) \ - safe_statfs(__FILE__, __LINE__, (path), (buf)) - -static inline off_t safe_lseek(const char *file, const int lineno, - int fd, off_t offset, int whence) -{ - off_t rval; - - rval = lseek(fd, offset, whence); - - if (rval == (off_t) -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "lseek(%d,%ld,%d) failed", fd, (long)offset, whence); - } else if (rval < 0) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid lseek(%d,%ld,%d) return value %ld", fd, - (long)offset, whence, (long)rval); - } - - return rval; -} -#define SAFE_LSEEK(fd, offset, whence) \ - safe_lseek(__FILE__, __LINE__, (fd), (offset), (whence)) - -static inline int safe_getrlimit(const char *file, const int lineno, - int resource, struct rlimit *rlim) -{ - int rval; - - rval = getrlimit(resource, rlim); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "getrlimit(%d,%p) failed", resource, rlim); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid getrlimit(%d,%p) return value %d", resource, - rlim, rval); - } - - return rval; -} -#define SAFE_GETRLIMIT(resource, rlim) \ - safe_getrlimit(__FILE__, __LINE__, (resource), (rlim)) - -static inline int safe_setrlimit(const char *file, const int lineno, - int resource, const struct rlimit *rlim) -{ - int rval; - - rval = setrlimit(resource, rlim); - - if (rval == -1) { - tst_brk_(file, lineno, TBROK | TERRNO, - "setrlimit(%d,%p) failed", resource, rlim); - } else if (rval) { - tst_brk_(file, lineno, TBROK | TERRNO, - "Invalid setrlimit(%d,%p) return value %d", resource, - rlim, rval); - } - - return rval; -} -#define SAFE_SETRLIMIT(resource, rlim) \ - safe_setrlimit(__FILE__, __LINE__, (resource), (rlim)) +#define SAFE_MPROTECT(addr, len, prot) \ + safe_mprotect(__FILE__, __LINE__, (addr), (len), (prot)) typedef void (*sighandler_t)(int); sighandler_t safe_signal(const char *file, const int lineno, @@ -646,9 +447,20 @@ int safe_unshare(const char *file, const int lineno, int flags); int safe_setns(const char *file, const int lineno, int fd, int nstype); #define SAFE_SETNS(fd, nstype) safe_setns(__FILE__, __LINE__, (fd), (nstype)) +/* + * SAFE_CMD() is a wrapper for tst_cmd(). It runs a command passed via argv[] + * and handles non-zero exit (exits with 'TBROK') and 'ENOENT' (the program not + * in '$PATH', exits with 'TCONF'). + * + * @param argv[] a 'NULL' terminated array of strings starting with the program + * name which is followed by optional arguments. + * @param stdout_path: path where to redirect stdout. Set NULL if redirection is + * not needed. + * @param stderr_path: path where to redirect stderr. Set NULL if redirection is + * not needed. + */ void safe_cmd(const char *file, const int lineno, const char *const argv[], const char *stdout_path, const char *stderr_path); - #define SAFE_CMD(argv, stdout_path, stderr_path) \ safe_cmd(__FILE__, __LINE__, (argv), (stdout_path), (stderr_path)) /* @@ -665,4 +477,44 @@ int safe_sysinfo(const char *file, const int lineno, struct sysinfo *info); #define SAFE_SYSINFO(info) \ safe_sysinfo(__FILE__, __LINE__, (info)) -#endif /* SAFE_MACROS_H__ */ +void safe_print_file(const char *file, const int lineno, char *path); + +int safe_sscanf(const char *file, const int lineno, const char *restrict buffer, + const char *restrict format, ...); +#define SAFE_SSCANF(buffer, format, ...) \ + safe_sscanf(__FILE__, __LINE__, (buffer), (format), ##__VA_ARGS__) + +int safe_prctl(const char *file, const int lineno, + int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); +#define SAFE_PRCTL(option, arg2, arg3, arg4, arg5) \ + safe_prctl(__FILE__, __LINE__, (option), (arg2), (arg3), (arg4), (arg5)) + +int safe_symlinkat(const char *file, const int lineno, + const char *oldpath, const int newdirfd, const char *newpath); + +#define SAFE_SYMLINKAT(oldpath, newdirfd, newpath) \ + safe_symlinkat(__FILE__, __LINE__, (oldpath), (newdirfd), (newpath)) + +ssize_t safe_readv(const char *file, const int lineno, char len_strict, + int fildes, const struct iovec *iov, int iovcnt); +#define SAFE_READV(len_strict, fildes, iov, iovcnt) \ + safe_readv(__FILE__, __LINE__, (len_strict), (fildes), \ + (iov), (iovcnt)) + +ssize_t safe_writev(const char *file, const int lineno, char len_strict, + int fildes, const struct iovec *iov, int iovcnt); +#define SAFE_WRITEV(len_strict, fildes, iov, iovcnt) \ + safe_writev(__FILE__, __LINE__, (len_strict), (fildes), \ + (iov), (iovcnt)) + +char *safe_ptsname(const char *const file, const int lineno, int masterfd); +#define SAFE_PTSNAME(masterfd) \ + safe_ptsname(__FILE__, __LINE__, (masterfd)) + +int safe_statvfs(const char *file, const int lineno, + const char *path, struct statvfs *buf); +#define SAFE_STATVFS(path, buf) \ + safe_statvfs(__FILE__, __LINE__, (path), (buf)) + +#endif /* TST_SAFE_MACROS_H__ */ diff --git a/include/tst_safe_macros_inline.h b/include/tst_safe_macros_inline.h new file mode 100644 index 00000000..15b75686 --- /dev/null +++ b/include/tst_safe_macros_inline.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2010-2024 Linux Test Project + * Copyright (c) 2011-2015 Cyril Hrubis + */ + +#ifndef TST_SAFE_MACROS_INLINE_H__ +#define TST_SAFE_MACROS_INLINE_H__ + +/* + * Following functions are inline because the behaviour may depend on + * -D_FILE_OFFSET_BITS=64 compile flag (type off_t or structures containing + * off_t fields), see man off_t(3type). + * + * Do not add other functions here. + */ + +static inline int safe_ftruncate(const char *file, const int lineno, + int fd, off_t length) +{ + int rval; + + rval = ftruncate(fd, length); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "ftruncate(%d,%ld) failed", fd, (long)length); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid ftruncate(%d,%ld) return value %d", fd, + (long)length, rval); + } + + return rval; +} + +#define SAFE_FTRUNCATE(fd, length) \ + safe_ftruncate(__FILE__, __LINE__, (fd), (length)) + +static inline int safe_posix_fadvise(const char *file, const int lineno, + int fd, off_t offset, off_t len, int advice) +{ + int rval; + + rval = posix_fadvise(fd, offset, len, advice); + + if (rval) + tst_brk_(file, lineno, TBROK, + "posix_fadvise(%d,%ld,%ld,%d) failed: %s", + fd, (long)offset, (long)len, advice, tst_strerrno(rval)); + + return rval; +} + +#define SAFE_POSIX_FADVISE(fd, offset, len, advice) \ + safe_posix_fadvise(__FILE__, __LINE__, (fd), (offset), (len), (advice)) + +static inline int safe_truncate(const char *file, const int lineno, + const char *path, off_t length) +{ + int rval; + + rval = truncate(path, length); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "truncate(%s,%ld) failed", path, (long)length); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid truncate(%s,%ld) return value %d", path, + (long)length, rval); + } + + return rval; +} + +#define SAFE_TRUNCATE(path, length) \ + safe_truncate(__FILE__, __LINE__, (path), (length)) + +static inline int safe_stat(const char *file, const int lineno, + const char *path, struct stat *buf) +{ + int rval; + + rval = stat(path, buf); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "stat(%s,%p) failed", path, buf); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid stat(%s,%p) return value %d", path, buf, + rval); + } + + return rval; +} + +#define SAFE_STAT(path, buf) \ + safe_stat(__FILE__, __LINE__, (path), (buf)) + +static inline int safe_fstat(const char *file, const int lineno, + int fd, struct stat *buf) +{ + int rval; + + rval = fstat(fd, buf); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "fstat(%d,%p) failed", fd, buf); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid fstat(%d,%p) return value %d", fd, buf, rval); + } + + return rval; +} +#define SAFE_FSTAT(fd, buf) \ + safe_fstat(__FILE__, __LINE__, (fd), (buf)) + +static inline int safe_lstat(const char *file, const int lineno, + const char *path, struct stat *buf) +{ + int rval; + + rval = lstat(path, buf); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "lstat(%s,%p) failed", path, buf); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid lstat(%s,%p) return value %d", path, buf, + rval); + } + + return rval; +} +#define SAFE_LSTAT(path, buf) \ + safe_lstat(__FILE__, __LINE__, (path), (buf)) + +static inline int safe_statfs(const char *file, const int lineno, + const char *path, struct statfs *buf) +{ + int rval; + + rval = statfs(path, buf); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "statfs(%s,%p) failed", path, buf); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid statfs(%s,%p) return value %d", path, buf, + rval); + } + + return rval; +} + +#define SAFE_STATFS(path, buf) \ + safe_statfs(__FILE__, __LINE__, (path), (buf)) + +static inline off_t safe_lseek(const char *file, const int lineno, + int fd, off_t offset, int whence) +{ + off_t rval; + + rval = lseek(fd, offset, whence); + + if (rval == (off_t) -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "lseek(%d,%ld,%d) failed", fd, (long)offset, whence); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid lseek(%d,%ld,%d) return value %ld", fd, + (long)offset, whence, (long)rval); + } + + return rval; +} + +#define SAFE_LSEEK(fd, offset, whence) \ + safe_lseek(__FILE__, __LINE__, (fd), (offset), (whence)) + +static inline int safe_getrlimit(const char *file, const int lineno, + int resource, struct rlimit *rlim) +{ + int rval; + + rval = getrlimit(resource, rlim); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "getrlimit(%d,%p) failed", resource, rlim); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid getrlimit(%d,%p) return value %d", resource, + rlim, rval); + } + + return rval; +} + +#define SAFE_GETRLIMIT(resource, rlim) \ + safe_getrlimit(__FILE__, __LINE__, (resource), (rlim)) + +static inline int safe_setrlimit(const char *file, const int lineno, + int resource, const struct rlimit *rlim) +{ + int rval; + + rval = setrlimit(resource, rlim); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "setrlimit(%d,%p) failed", resource, rlim); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid setrlimit(%d,%p) return value %d", resource, + rlim, rval); + } + + return rval; +} + +#define SAFE_SETRLIMIT(resource, rlim) \ + safe_setrlimit(__FILE__, __LINE__, (resource), (rlim)) + +void tst_prot_to_str(const int prot, char *buf); + +static inline void *safe_mmap(const char *file, const int lineno, + void *addr, size_t length, int prot, int flags, int fd, off_t offset) +{ + void *rval; + char prot_buf[512]; + + tst_prot_to_str(prot, prot_buf); + + tst_res_(file, lineno, TDEBUG, + "mmap(%p, %zu, %s(%x), %d, %d, %lld)", + addr, length, prot_buf, prot, flags, fd, (long long int)offset); + + rval = mmap(addr, length, prot, flags, fd, offset); + if (rval == MAP_FAILED) { + tst_brk_(file, lineno, TBROK | TERRNO, + "mmap(%p,%zu,%s(%x),%d,%d,%ld) failed", + addr, length, prot_buf, prot, flags, fd, (long) offset); + } + + return rval; +} + +#define SAFE_MMAP(addr, length, prot, flags, fd, offset) \ + safe_mmap(__FILE__, __LINE__, (addr), (length), (prot), \ + (flags), (fd), (offset)) + +#endif /* TST_SAFE_MACROS_INLINE_H__ */ diff --git a/include/tst_safe_prw.h b/include/tst_safe_prw.h index 2e506cb4..349fb46b 100755 --- a/include/tst_safe_prw.h +++ b/include/tst_safe_prw.h @@ -5,6 +5,8 @@ #ifndef TST_SAFE_PRW_H__ #define TST_SAFE_PRW_H__ +#include "lapi/uio.h" + static inline ssize_t safe_pread(const char *file, const int lineno, char len_strict, int fildes, void *buf, size_t nbyte, off_t offset) @@ -52,4 +54,60 @@ static inline ssize_t safe_pwrite(const char *file, const int lineno, safe_pwrite(__FILE__, __LINE__, (len_strict), (fildes), \ (buf), (nbyte), (offset)) +static inline ssize_t safe_preadv(const char *file, const int lineno, + char len_strict, int fildes, const struct iovec *iov, int iovcnt, + off_t offset) +{ + ssize_t rval, nbyte; + int i; + + for (i = 0, nbyte = 0; i < iovcnt; i++) + nbyte += iov[i].iov_len; + + rval = preadv(fildes, iov, iovcnt, offset); + + if (rval == -1 || (len_strict && rval != nbyte)) { + tst_brk_(file, lineno, TBROK | TERRNO, + "preadv(%d,%p,%d,%lld) failed", + fildes, iov, iovcnt, (long long)offset); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid preadv(%d,%p,%d,%lld) return value %zd", + fildes, iov, iovcnt, (long long)offset, rval); + } + + return rval; +} +#define SAFE_PREADV(len_strict, fildes, iov, iovcnt, offset) \ + safe_preadv(__FILE__, __LINE__, (len_strict), (fildes), \ + (iov), (iovcnt), (offset)) + +static inline ssize_t safe_pwritev(const char *file, const int lineno, + char len_strict, int fildes, const struct iovec *iov, int iovcnt, + off_t offset) +{ + ssize_t rval, nbyte; + int i; + + for (i = 0, nbyte = 0; i < iovcnt; i++) + nbyte += iov[i].iov_len; + + rval = pwritev(fildes, iov, iovcnt, offset); + + if (rval == -1 || (len_strict && rval != nbyte)) { + tst_brk_(file, lineno, TBROK | TERRNO, + "pwritev(%d,%p,%d,%lld) failed", + fildes, iov, iovcnt, (long long)offset); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid pwritev(%d,%p,%d,%lld) return value %zd", + fildes, iov, iovcnt, (long long)offset, rval); + } + + return rval; +} +#define SAFE_PWRITEV(len_strict, fildes, iov, iovcnt, offset) \ + safe_pwritev(__FILE__, __LINE__, (len_strict), (fildes), \ + (iov), (iovcnt), (offset)) + #endif /* SAFE_PRW_H__ */ diff --git a/include/tst_security.h b/include/tst_security.h new file mode 100644 index 00000000..e2d7270d --- /dev/null +++ b/include/tst_security.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) Linux Test Project, 2020-2024 + */ + +#ifndef TST_SECURITY_H__ +#define TST_SECURITY_H__ + +#define LSM_SYS_FILE "/sys/kernel/security/lsm" + +int tst_lsm_enabled(const char *name); + +/* + * Detect whether FIPS enabled + * @return 0: FIPS not enabled, 1: FIPS enabled + */ +int tst_fips_enabled(void); + +int tst_lockdown_enabled(void); +int tst_secureboot_enabled(void); +int tst_selinux_enforcing(void); + +#endif /* TST_SECURITY_H__ */ diff --git a/include/tst_sys_conf.h b/include/tst_sys_conf.h index 4c85767b..a221a9a0 100755 --- a/include/tst_sys_conf.h +++ b/include/tst_sys_conf.h @@ -28,4 +28,38 @@ int tst_sys_conf_save(const struct tst_path_val *conf); void tst_sys_conf_restore(int verbose); void tst_sys_conf_dump(void); +int tst_read_bool_sys_param(const char *filename); + +/** + * TST_SYS_CONF_LONG_SET() - Writes a long int into a sys or proc file. + * + * @path: A path to a sysfs or a procfs file. + * @val: A long int value to be written to the file. + * @check: If non-zero the library reads the file back and checks that the + * value is the one we have written there. If not the library calls + * tst_brk(TBROK, ...). + * + * Sets a sysfs or procfs file and optionally checks that it was set correctly. + */ +#define TST_SYS_CONF_LONG_SET(path, val, check) \ + tst_sys_conf_long_set_(__FILE__, __LINE__, path, val, check) + +void tst_sys_conf_long_set_(const char *file, const int lineno, + const char *path, long val, int check); + + +/** + * TST_SYS_CONF_LONG_GET() - Reads a long int from sys or proc file. + * + * @path: A path to a sysfs or a procfs file. + * return: A value read from the file converted into a long. + * + * Gets a sysfs or procfs file value and converts it to long. + */ +#define TST_SYS_CONF_LONG_GET(path) \ + tst_sys_conf_long_get_(__FILE__, __LINE__, path) + +long tst_sys_conf_long_get_(const char *file, const int lineno, + const char *path); + #endif diff --git a/include/tst_taint.h b/include/tst_taint.h index bd8076c1..b2b20168 100755 --- a/include/tst_taint.h +++ b/include/tst_taint.h @@ -64,6 +64,7 @@ #define TST_TAINT_K (1 << 15) /* kernel has been live-patched */ #define TST_TAINT_X (1 << 16) /* auxiliary taint, for distro's use */ #define TST_TAINT_T (1 << 17) /* kernel was built with the struct randomization plugin */ +#define TST_TAINT_N (1 << 18) /* an in-kernel test has been run */ /* * Initialize and prepare support for checking tainted kernel. Called diff --git a/include/tst_test.h b/include/tst_test.h index 75c2109b..9c21c172 100755 --- a/include/tst_test.h +++ b/include/tst_test.h @@ -1,7 +1,7 @@ -// SPDX-License-Identifier: GPL-2.0-or-later +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2015-2016 Cyril Hrubis - * Copyright (c) Linux Test Project, 2016-2019 + * Copyright (c) Linux Test Project, 2016-2024 */ #ifndef TST_TEST_H__ @@ -15,9 +15,11 @@ #include #include #include +#include #include "tst_common.h" #include "tst_res_flags.h" +#include "tst_parse.h" #include "tst_test_macros.h" #include "tst_checkpoint.h" #include "tst_device.h" @@ -39,55 +41,113 @@ #include "tst_capability.h" #include "tst_hugepage.h" #include "tst_assert.h" -#include "tst_lockdown.h" -#include "tst_fips.h" +#include "tst_security.h" #include "tst_taint.h" #include "tst_memutils.h" #include "tst_arch.h" +#include "tst_fd.h" +#include "tst_tmpdir.h" -/* - * Reports testcase result. - */ void tst_res_(const char *file, const int lineno, int ttype, const char *fmt, ...) __attribute__ ((format (printf, 4, 5))); +/** + * tst_res() - Reports a test result. + * + * @ttype: An &enum tst_res_flags. + * @arg_fmt: A printf-like format. + * @...: A printf-like parameters. + * + * This is the main test reporting function. Each time this function is called + * with one of TPASS, TFAIL, TCONF, TBROK or TWARN a counter in page of shared + * memory is incremented. This means that there is no need to propagate test + * results from children and that results are accounted for once this function + * returns. The counters are incremented atomically which makes this function + * thread-safe. + */ #define tst_res(ttype, arg_fmt, ...) \ ({ \ - TST_RES_SUPPORTS_TCONF_TFAIL_TINFO_TPASS_TWARN(!((TTYPE_RESULT(ttype) ?: TCONF) & \ - (TCONF | TFAIL | TINFO | TPASS | TWARN))); \ + TST_RES_SUPPORTS_TCONF_TDEBUG_TFAIL_TINFO_TPASS_TWARN(\ + !((TTYPE_RESULT(ttype) ?: TCONF) & \ + (TCONF | TDEBUG | TFAIL | TINFO | TPASS | TWARN))); \ tst_res_(__FILE__, __LINE__, (ttype), (arg_fmt), ##__VA_ARGS__);\ }) void tst_resm_hexd_(const char *file, const int lineno, int ttype, const void *buf, size_t size, const char *arg_fmt, ...) __attribute__ ((format (printf, 6, 7))); - +/** + * tst_res_hexd() - Reports a test result along with hex dump of a buffer. + * + * This call is the same as tst_res() but includes a pointer and size of the + * buffer that is going to be printed in the output in a hexadecimal format. + * + * @ttype: An &enum tst_res_flags. + * @buf: A pointer to a buffer to print in hexadecimal format. + * @size: A size of the buffer. + * @arg_fmt: A printf-like format. + * @...: A printf-like parameters. + */ #define tst_res_hexd(ttype, buf, size, arg_fmt, ...) \ tst_resm_hexd_(__FILE__, __LINE__, (ttype), (buf), (size), \ (arg_fmt), ##__VA_ARGS__) -/* - * Reports result and exits a test. - */ void tst_brk_(const char *file, const int lineno, int ttype, const char *fmt, ...) __attribute__ ((format (printf, 4, 5))); -#define tst_brk(ttype, arg_fmt, ...) \ - ({ \ - TST_BRK_SUPPORTS_ONLY_TCONF_TBROK(!((ttype) & \ - (TBROK | TCONF | TFAIL))); \ - tst_brk_(__FILE__, __LINE__, (ttype), (arg_fmt), ##__VA_ARGS__);\ - }) +/** + * tst_brk() - Reports a breakage and exits the test or test process. + * + * @ttype: An &enum tst_res_flags. + * @arg_fmt: A printf-like format. + * @...: A printf-like parameters. + * + * Reports a single result and exits immediately. The call behaves differently + * based on the ttype parameter. For all ttype results but TBROK the call exits + * the current test process, i.e. increments test result counters and calls + * exit(0). + * + * The TBROK ttype is special that apart from exiting the current test process + * it also tells to the test library to exit immediately. When TBROK is + * triggered by any of the test processes the whole process group is killed so + * that there are no processes left after the library process exits. This also + * means that any subsequent test iterations are not executed, e.g. if a test + * runs for all filesystems and tst_brk() with TBROK is called, the test exits + * and does not attempt to continue a test iteration for the next filesystem. + * + * When test is in cleanup() function TBROK is converted into TWARN by the test + * library and we attempt to carry on with a cleanup even when tst_brk() was + * called. This makes it possible to use SAFE_FOO() macros in the test cleanup + * without interrupting the cleanup process on a failure. + */ +#define tst_brk(ttype, arg_fmt, ...) \ + tst_brk_(__FILE__, __LINE__, (ttype), (arg_fmt), ##__VA_ARGS__) void tst_printf(const char *const fmt, ...) __attribute__((nonnull(1), format (printf, 1, 2))); -/* flush stderr and stdout */ +/** + * tst_flush() - Flushes the output file streams. + * + * There are rare cases when we want to flush the output file streams + * explicitly, e.g. before we do an action that may crash the test to ensure + * that the messages have been written out. + * + * This is also called by the SAFE_FORK() because otherwise each child would + * end up with the same copy of the file in it's memory and any messages in + * buffers would be multiplied. + */ void tst_flush(void); pid_t safe_fork(const char *filename, unsigned int lineno); +/** + * SAFE_FORK() - Forks a test child. + * + * This call makes sure that output file streams are flushed and also handles + * errors from fork(). Use this instead of fork() whenever possible! + */ #define SAFE_FORK() \ safe_fork(__FILE__, __LINE__) @@ -95,15 +155,30 @@ pid_t safe_fork(const char *filename, unsigned int lineno); ({int ret = expr; \ ret != 0 ? tst_res(TINFO, #expr " failed"), ret : ret; }) \ -/* - * Functions to convert ERRNO to its name and SIGNAL to its name. +/** + * tst_strerrno() - Converts an errno number into a name. + * + * @err: An errno number. + * return: An errno name e.g. "EINVAL". */ const char *tst_strerrno(int err); -const char *tst_strsig(int sig); -/* - * Returns string describing status as returned by wait(). + +/** + * tst_strsig() - Converts a signal number into a name. * - * BEWARE: Not thread safe. + * @sig: A signal number. + * return: A signal name e.g. "SIGINT". + */ +const char *tst_strsig(int sig); + + +/** + * tst_strstatus() - Returns string describing status as returned by wait(). + * + * WARNING: Not thread safe. + * + * @status: A status as returned by wait() + * return: A string description for the status e.g. "killed by SIGKILL". */ const char *tst_strstatus(int status); @@ -113,30 +188,49 @@ const char *tst_strstatus(int status); #include "tst_clone.h" #include "tst_cgroup.h" -/* - * Wait for all children and exit with TBROK if - * any of them returned a non-zero exit status. +/** + * tst_reap_children() - Waits for all child processes to exit. + * + * Wait for all children and exit with TBROK if any of them returned a non-zero + * exit status. */ void tst_reap_children(void); +/** + * struct tst_option - Test command line option. + * + * @optstr: A short command line option, e.g. "a" or "a:". + * @arg: A pointer to store the option value to. + * @help: A help string for the option displayed when test is passed '-h' on + * the command-line. + */ struct tst_option { char *optstr; char **arg; char *help; }; -/* - * Options parsing helpers. +/** + * struct tst_tag - A test tag. * - * If str is NULL these are No-op. + * @name: A tag name. + * @value: A tag value. * - * On failure non-zero (errno) is returned. + * This structure is used to encode pointers to upstream commits in regression + * tests as well as CVE numbers or any additional useful hints. + * + * The content of these tags is printed by the test on a failure to help the + * testers with debugging. + * + * The supported tags are: + * + * - "linux-git" with first 12 numbers from an upstream kernel git hash. + * - "CVE" with a CVE number e.g. "2000-1234". + * - "glibc-git" with first 12 numbers from an upstream glibc git hash. + * - "musl-git" with first 12 numbers from an upstream musl git hash. + * - "known-fail" a message describing something that is supposed to work but + * rather than that produces a longstanding failures. */ -int tst_parse_int(const char *str, int *val, int min, int max); -int tst_parse_long(const char *str, long *val, long min, long max); -int tst_parse_float(const char *str, float *val, float min, float max); -int tst_parse_filesize(const char *str, long long *val, long long min, long long max); - struct tst_tag { const char *name; const char *value; @@ -144,214 +238,462 @@ struct tst_tag { extern unsigned int tst_variant; -#define TST_NO_HUGEPAGES ((unsigned long)-1) +#define TST_UNLIMITED_TIMEOUT (-1) -#define TST_UNLIMITED_RUNTIME (-1) +/** + * struct tst_ulimit_val - An ulimit resource and value. + * + * @resource: Which resource limits should be adjusted. See setrlimit(2) for + * the list of the RLIMIT_* constants. + * @rlim_cur: A limit value. + */ +struct tst_ulimit_val { + int resource; + rlim_t rlim_cur; +}; -struct tst_test { - /* number of tests available in test() function */ +/** + * struct tst_fs - A file system type, mkfs and mount options + * + * @type: A filesystem type to use. + * + * @mkfs_opts: A NULL terminated array of options passed to mkfs in the case + * of 'tst_test.format_device'. These options are passed to mkfs + * before the device path. + * + * @mkfs_size_opt: An option passed to mkfs in the case of + * 'tst_test.format_device'. The device size in blocks is + * passed to mkfs after the device path and can be used to + * limit the file system not to use the whole block device. + * + * @mkfs_ver: mkfs tool version. The string format supports relational + * operators such as < > <= >= ==. + * + * @mnt_flags: MS_* flags passed to mount(2) when the test library mounts a + * device in the case of 'tst_test.mount_device'. + * + * @mnt_data: The data passed to mount(2) when the test library mounts a device + * in the case of 'tst_test.mount_device'. + * + * @min_kver: A minimum kernel version supporting the filesystem which has been + * created with mkfs. + */ +struct tst_fs { + const char *type; + + const char *const *mkfs_opts; + const char *mkfs_size_opt; + const char *mkfs_ver; + + unsigned int mnt_flags; + const void *mnt_data; + + const char *min_kver; +}; + +/** + * struct tst_test - A test description. + * + * @tcnt: A number of tests. If set the test() callback is called tcnt times + * and each time passed an increasing counter value. + * @options: An NULL optstr terminated array of struct tst_option. + * + * @min_kver: A minimal kernel version the test can run on. e.g. "3.10". + * + * @supported_archs: A NULL terminated array of architectures the test runs on + * e.g. {"x86_64, "x86", NULL}. Calls tst_is_on_arch() to + * check if current CPU architecture is supported and exits + * the test with TCONF if it's not. + * + * @tconf_msg: If set the test exits with TCONF right after entering the test + * library. This is used by the TST_TEST_TCONF() macro to disable + * tests at compile time. + * + * @needs_tmpdir: If set a temporary directory is prepared for the test inside + * $TMPDIR and the test $CWD is set to point to it. The content + * of the temporary directory is removed automatically after + * the test is finished. + * + * @needs_root: If set the test exit with TCONF unless it's executed under root + * user. + * + * @forks_child: Has to be set if the test intends to fork children. + * + * @needs_device: If set a block device is prepared for the test, the device + * path and size are set in the struct tst_device variable + * called tst_device. If $LTP_DEV variable exists in the test + * environment the test attempts to use that device first and + * only if that fails the test falls back to use loop devices. + * This flag implies needs_tmpdir flag because loop device + * backing files are created in the test temporary directory. + * + * @needs_checkpoints: Has to be set if the test wants to use checkpoint + * synchronization primitives. + * + * @needs_overlay: If set overlay file system is mounted on the top of the + * file system at tst_test.mntpoint. + * + * @format_device: Does all tst_test.needs_device would do and also formats + * the device with a file system as well. + * + * @mount_device: Does all tst_test.format_device would do and also mounts the + * device at tst_test.mntpoint. + * + * @needs_rofs: If set a read-only file system is mounted at tst_test.mntpoint. + * + * @child_needs_reinit: Has to be set if the test needs to call tst_reinit() + * from a process started by exec(). + * + * @runs_script: Implies child_needs_reinit and forks_child at the moment. + * + * @needs_devfs: If set the devfs is mounted at tst_test.mntpoint. This is + * needed for tests that need to create device files since tmpfs + * at /tmp is usually mounted with 'nodev' option. + * + * @restore_wallclock: Saves wall clock at the start of the test and restores + * it at the end with the help of monotonic timers. + * Testcases that modify system wallclock use this to + * restore the system to the previous state. + * + * @all_filesystems: If set the test is executed for all supported filesytems, + * i.e. file system that is supported by the kernel and has + * mkfs installed on the system.The file system is mounted at + * tst_test.mntpoint and file system details, e.g. type are set + * in the struct tst_device. Each execution is independent, + * that means that for each iteration tst_test.setup() is + * called at the test start and tst_test.cleanup() is called + * at the end and tst_brk() only exits test for a single + * file system. That especially means that calling + * tst_brk(TCONF, ...) in the test setup will skip the + * current file system. + * + * @skip_in_lockdown: Skip the test if kernel lockdown is enabled. + * + * @skip_in_secureboot: Skip the test if secureboot is enabled. + * + * @skip_in_compat: Skip the test if we are executing 32bit binary on a 64bit + * kernel, i.e. we are testing the kernel compat layer. + * + * @needs_abi_bits: Skip the test if runs on a different kernel ABI than + * requested (on 32bit instead of 64bit or vice versa). + * Possible values: 32, 64. + * + * @needs_hugetlbfs: If set hugetlbfs is mounted at tst_test.mntpoint. + * + * @skip_filesystems: A NULL terminated array of unsupported file systems. The + * test reports TCONF if the file system to be tested is + * present in the array. This is especially useful to filter + * out unsupported file system when tst_test.all_filesystems + * is enabled. + * + * @min_cpus: Minimal number of online CPUs the test needs to run. + * + * @min_mem_avail: Minimal amount of available RAM memory in megabytes required + * for the test to run. + * + * @min_swap_avail: Minimal amount of available swap memory in megabytes + * required for the test to run. + * + * @hugepages: An interface to reserve hugepages prior running the test. + * Request how many hugepages should be reserved in the global + * pool and also if having hugepages is required for the test run + * or not, i.e. if test should exit with TCONF if the requested + * amount of hugepages cannot be reserved. If TST_REQUEST is set + * the library will try it's best to reserve the hugepages and + * return the number of available hugepages in tst_hugepages, which + * may be 0 if there is no free memory or hugepages are not + * supported at all. If TST_NEEDS the requested hugepages are + * required for the test and the test exits if it couldn't be + * required. It can also be used to disable hugepages by setting + * .hugepages = {TST_NO_HUGEPAGES}. The test library restores the + * original poll allocations after the test has finished. + * + * @taint_check: If set the test fails if kernel is tainted during the test run. + * That means tst_taint_init() is called during the test setup + * and tst_taint_check() at the end of the test. If all_filesystems + * is set taint check will be performed after each iteration and + * testing will be terminated by TBROK if taint is detected. + * + * @test_variants: If set denotes number of test variant, the test is executed + * variants times each time with tst_variant set to different + * number. This allows us to run the same test for different + * settings. The intended use is to test different syscall + * wrappers/variants but the API is generic and does not limit + * usage in any way. + * + * @dev_min_size: A minimal device size in megabytes. + * + * @filesystems: A NULL type terminated array of per file system type + * parameters for mkfs and mount. If the first entry type is NULL + * it describes a default parameters for all file system tests. + * The rest of the entries the describes per file system type + * parameters. If tst_test.all_filesystems is set, the test runs + * for all filesystems and uses the array to lookup the mkfs + * and mount options. If tst_test.all_filesystems is not set + * the test iterates over file system types defined in the array. + * If there is only a single entry in the array with a NULL type, + * the test runs just once for the default file sytem i.e. + * $TST_FS_TYPE. + * + * @mntpoint: A mount point where the test library mounts requested file system. + * The directory is created by the library, the test must not create + * it itself. + * + * @timeout: Maximum allowable time in seconds for the entire duration of a test. + * By default, the timeout limits the total time for setup, single test + * function invocation, and cleanup phases. However, if .runtime is + * explicitly set and tst_remaining_runtime() is used in the test's + * main loop, the timeout then applies only to the setup and cleanup + * phases, as the runtime separately limits the main test execution. + * This ensures the test does not hang indefinitely, in the rare case + * that the test timeout cannot be accurately determined, it can be + * set to a sufficiently large value or TST_UNLIMITED_TIMEOUT to remove + * the limit. + * + * @runtime: Maximum runtime in seconds for the test's main execution loop. + * This should be set for tests that are expected to run longer + * than a few seconds and call tst_remaining_runtime() in their + * main loop to exit gracefully when the runtime is exceeded. + * Tests may finish sooner if their task completes (e.g., reaching + * a requested number of iterations) before the runtime runs out. + * The runtime is fixed and does not scale with timeout multipliers + * (e.g., TIMEOUT_MUL), ensuring consistent test duration across + * different environments (e.g., debug vs. stock kernels). + * + * @min_runtime: Optional lower bound (in seconds) applied after runtime is + * scaled by LTP_RUNTIME_MUL. If the scaled runtime is smaller + * than this value, it will be clamped up to min_runtime. + * This is useful for tests that require a minimum execution + * time to gather sufficient samples or trigger events (e.g., + * probabilistic or fuzzy synchronization tests). + * If not set, a default minimum of 1 second is enforced. + * + * @setup: Setup callback is called once at the start of the test in order to + * prepare the test environment. + * + * @cleanup: Cleanup callback is either called at the end of the test, or in a + * case that tst_brk() was called. That means that cleanup must be + * able to handle all possible states the test can be in. This + * usually means that we have to check if file descriptor was opened + * before we attempt to close it, etc. + * + * + * @test: A main test function, only one of the tst_test.test and test_all can + * be set. When this function is set the tst_test.tcnt must be set to a + * positive integer and this function will be executed tcnt times + * during a single test iteration. May be executed several times if test + * was passed '-i' or '-d' command line parameters. + * + * @test_all: A main test function, only one of the tst_test.test and test_all + * can be set. May be executed several times if test was passed '-i' + * or '-d' command line parameters. + * + * @scall: Internal only (timer measurement library). + * + * @sample: Internal only (timer measurement library). + * + * @resource_files: A NULL terminated array of filenames that will be copied + * to the test temporary directory from the LTP datafiles + * directory. + * + * @needs_drivers: A NULL terminated array of kernel modules required to run + * the test. The module has to be build in or present in order + * for the test to run. + * + * @save_restore: A {} terminated array of /proc or /sys files that should + * saved at the start of the test and restored at the end. See + * tst_sys_conf_save() and struct tst_path_val for details. + * + * @ulimit: A {} terminated array of process limits RLIMIT_* to be adjusted for + * the test. + * + * @needs_kconfigs: A NULL terminated array of kernel config options that are + * required for the test. All strings in the array have to be + * evaluated to true for the test to run. Boolean operators + * and parenthesis are supported, e.g. + * "CONFIG_X86_INTEL_UMIP=y | CONFIG_X86_UIMP=y" is evaluated + * to true if at least one of the options is present. + * + * @bufs: A description of guarded buffers to be allocated for the test. Guarded + * buffers are buffers with poisoned page allocated right before the start + * of the buffer and canary right after the end of the buffer. See + * struct tst_buffers and tst_buffer_alloc() for details. + * + * @caps: A {} terminated array of capabilities to change before the start of + * the test. See struct tst_cap and tst_cap_setup() for details. + * + * @tags: A {} terminated array of test tags. See struct tst_tag for details. + * + * @needs_cmds: A NULL terminated array of commands required for the test to run. + * + * @needs_cgroup_ver: If set the test will run only if the specified cgroup + * version is present on the system. + * + * @needs_cgroup_ctrls: A {} terminated array of cgroup controllers the test + * needs to run. + * + * @needs_cgroup_nsdelegate: If set test the will run only if cgroup2 is mounted + * with nsdelegate option. + */ + + struct tst_test { unsigned int tcnt; struct tst_option *options; const char *min_kver; - /* - * The supported_archs is a NULL terminated list of archs the test - * does support. - */ const char *const *supported_archs; - /* If set the test is compiled out */ const char *tconf_msg; - int needs_tmpdir:1; - int needs_root:1; - int forks_child:1; - int needs_device:1; - int needs_checkpoints:1; - int needs_overlay:1; - int format_device:1; - int mount_device:1; - int needs_rofs:1; - int child_needs_reinit:1; - int needs_devfs:1; - int restore_wallclock:1; + unsigned int needs_tmpdir:1; + unsigned int needs_root:1; + unsigned int forks_child:1; + unsigned int needs_device:1; + unsigned int needs_checkpoints:1; + unsigned int needs_overlay:1; + unsigned int format_device:1; + unsigned int mount_device:1; + unsigned int needs_rofs:1; + unsigned int child_needs_reinit:1; + unsigned int runs_script:1; + unsigned int needs_devfs:1; + unsigned int restore_wallclock:1; - /* - * If set the test function will be executed for all available - * filesystems and the current filesystem type would be set in the - * tst_device->fs_type. - * - * The test setup and cleanup are executed before/after __EACH__ call - * to the test function. - */ - int all_filesystems:1; + unsigned int all_filesystems:1; - int skip_in_lockdown:1; - int skip_in_secureboot:1; - int skip_in_compat:1; + unsigned int skip_in_lockdown:1; + unsigned int skip_in_secureboot:1; + unsigned int skip_in_compat:1; - /* - * If set, the hugetlbfs will be mounted at .mntpoint. - */ - int needs_hugetlbfs:1; - /* - * The skip_filesystems is a NULL terminated list of filesystems the - * test does not support. It can also be used to disable whole class of - * filesystems with a special keywords such as "fuse". - */ + int needs_abi_bits; + + unsigned int needs_hugetlbfs:1; + const char *const *skip_filesystems; - /* Minimum number of online CPU required by the test */ unsigned long min_cpus; - - /* Minimum size(MB) of MemAvailable required by the test */ unsigned long min_mem_avail; - - /* Minimum size(MB) of SwapFree required by the test */ unsigned long min_swap_avail; - /* - * Two policies for reserving hugepage: - * - * TST_REQUEST: - * It will try the best to reserve available huge pages and return the number - * of available hugepages in tst_hugepages, which may be 0 if hugepages are - * not supported at all. - * - * TST_NEEDS: - * This is an enforced requirement, LTP should strictly do hpages applying and - * guarantee the 'HugePages_Free' no less than pages which makes that test can - * use these specified numbers correctly. Otherwise, test exits with TCONF if - * the attempt to reserve hugepages fails or reserves less than requested. - * - * With success test stores the reserved hugepage number in 'tst_hugepages. For - * the system without hugetlb supporting, variable 'tst_hugepages' will be set to 0. - * If the hugepage number needs to be set to 0 on supported hugetlb system, please - * use '.hugepages = {TST_NO_HUGEPAGES}'. - * - * Also, we do cleanup and restore work for the hpages resetting automatically. - */ struct tst_hugepage hugepages; - /* - * If set to non-zero, call tst_taint_init(taint_check) during setup - * and check kernel taint at the end of the test. If all_filesystems - * is non-zero, taint check will be performed after each FS test and - * testing will be terminated by TBROK if taint is detected. - */ unsigned int taint_check; - /* - * If set non-zero denotes number of test variant, the test is executed - * variants times each time with tst_variant set to different number. - * - * This allows us to run the same test for different settings. The - * intended use is to test different syscall wrappers/variants but the - * API is generic and does not limit the usage in any way. - */ unsigned int test_variants; - /* Minimal device size in megabytes */ unsigned int dev_min_size; - /* Device filesystem type override NULL == default */ - const char *dev_fs_type; + struct tst_fs *filesystems; - /* Options passed to SAFE_MKFS() when format_device is set */ - const char *const *dev_fs_opts; - const char *const *dev_extra_opts; - - /* Device mount options, used if mount_device is set */ const char *mntpoint; - unsigned int mnt_flags; - void *mnt_data; - /* - * Maximal test runtime in seconds. - * - * Any test that runs for more than a second or two should set this and - * also use tst_remaining_runtime() to exit when runtime was used up. - * Tests may finish sooner, for example if requested number of - * iterations was reached before the runtime runs out. - * - * If test runtime cannot be know in advance it should be set to - * TST_UNLIMITED_RUNTIME. - */ - int max_runtime; + int timeout; + int runtime; + int min_runtime; void (*setup)(void); void (*cleanup)(void); - void (*test)(unsigned int test_nr); void (*test_all)(void); - /* Syscall name used by the timer measurement library */ const char *scall; - - /* Sampling function for timer measurement testcases */ int (*sample)(int clk_id, long long usec); - /* NULL terminated array of resource file names */ const char *const *resource_files; - - /* NULL terminated array of needed kernel drivers */ const char * const *needs_drivers; - /* - * {NULL, NULL} terminated array of (/proc, /sys) files to save - * before setup and restore after cleanup - */ const struct tst_path_val *save_restore; - /* - * NULL terminated array of kernel config options required for the - * test. - */ + const struct tst_ulimit_val *ulimit; + const char *const *needs_kconfigs; - /* - * {NULL, NULL} terminated array to be allocated buffers. - */ struct tst_buffers *bufs; - /* - * {NULL, NULL} terminated array of capability settings - */ struct tst_cap *caps; - /* - * {NULL, NULL} terminated array of tags. - */ const struct tst_tag *tags; - /* NULL terminated array of required commands */ const char *const *needs_cmds; - /* Requires a particular CGroup API version. */ const enum tst_cg_ver needs_cgroup_ver; - /* {} terminated array of required CGroup controllers */ const char *const *needs_cgroup_ctrls; + + unsigned int needs_cgroup_nsdelegate:1; }; -/* - * Runs tests. +/** + * tst_run_tcases() - Entry point to the test library. + * + * @argc: An argc that was passed to the main(). + * @argv: An argv that was passed to the main(). + * @self: The test description and callbacks packed in the struct tst_test + * structure. + * + * A main() function which calls this function is added to each test + * automatically by including the tst_test.h header. The test has to define the + * struct tst_test called test. + * + * This function does not return, i.e. calls exit() after the test has finished. */ void tst_run_tcases(int argc, char *argv[], struct tst_test *self) __attribute__ ((noreturn)); #define IPC_ENV_VAR "LTP_IPC_PATH" -/* - * Does library initialization for child processes started by exec() +/** + * tst_reinit() - Reinitialize the test library. * - * The LTP_IPC_PATH variable must be passed to the program environment. + * In a cases where a test child process calls exec() it no longer can access + * the test library shared memory and therefore use the test reporting + * functions, checkpoint library, etc. This function re-initializes the test + * library so that it can be used again. + * + * @important The LTP_IPC_PATH variable must be passed to the program environment. */ void tst_reinit(void); +/** + * tst_run_script() - Prepare the environment and execute a (shell) script. + * + * @script_name: A filename of the script. + * @params: A NULL terminated array of (shell) script parameters, pass NULL if + * none are needed. This what is passed starting from argv[1]. + * + * The (shell) script is executed with LTP_IPC_PATH in environment so that the + * binary helpers such as tst_res_ or tst_checkpoint work properly when executed + * from the script. This also means that the tst_test.runs_script flag needs to + * be set. + * + * A shell script has to source the tst_env.sh shell script at the start and + * after that it's free to use tst_res in the same way C code would use. + * + * Example shell script that reports success:: + * + * #!/bin/sh + * . tst_env.sh + * + * tst_res TPASS "Example test works" + * + * The call returns a pid in a case that you want to examine the return value + * of the script yourself. If you do not need to check the return value + * yourself you can use tst_reap_children() to wait for the completion. Or let + * the test library collect the child automatically, just be wary that the + * script and the test both runs concurently at the same time in this case. + * + * Return: A pid of the (shell) script process. + */ +int tst_run_script(const char *script_name, char *const params[]); + +/* + * Sets entire timeout in seconds. + */ +void tst_set_timeout(int timeout); + unsigned int tst_multiply_timeout(unsigned int timeout); /* @@ -366,18 +708,13 @@ unsigned int tst_remaining_runtime(void); /* * Sets maximal test runtime in seconds. */ -void tst_set_max_runtime(int max_runtime); +void tst_set_runtime(int runtime); /* * Create and open a random file inside the given dir path. * It unlinks the file after opening and return file descriptor. */ -int tst_creat_unlinked(const char *path, int flags); - -/* - * Returns path to the test temporary directory in a newly allocated buffer. - */ -char *tst_get_tmpdir(void); +int tst_creat_unlinked(const char *path, int flags, mode_t mode); /* * Returns path to the test temporary directory root (TMPDIR). @@ -403,6 +740,14 @@ int main(int argc, char *argv[]) #endif /* TST_NO_DEFAULT_MAIN */ +/** + * TST_TEST_TCONF() - Exit tests with a TCONF message. + * + * @message: Error message (the reason to skip test). + * + * This macro is used in test that couldn't be compiled either because current + * CPU architecture is unsupported or because of missing development libraries. + */ #define TST_TEST_TCONF(message) \ static struct tst_test test = { .tconf_msg = message } \ diff --git a/include/tst_test_macros.h b/include/tst_test_macros.h index bd0c491c..be963ed9 100755 --- a/include/tst_test_macros.h +++ b/include/tst_test_macros.h @@ -1,12 +1,39 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2015-2020 Cyril Hrubis - * Copyright (c) Linux Test Project, 2021-2022 + * Copyright (c) 2015-2024 Cyril Hrubis + * Copyright (c) Linux Test Project, 2021-2024 + */ + +/** + * DOC: tst_test_macros.h -- helpers for testing syscalls */ #ifndef TST_TEST_MACROS_H__ #define TST_TEST_MACROS_H__ +#include + +extern long TST_RET; +extern void *TST_RET_PTR; +extern int TST_ERR; +extern int TST_PASS; + +/** + * TEST() - Store syscall retval long and errno. + * + * @SCALL: Tested syscall e.g. write(fd, buf, len). + * + * This macro is a shortcut for storing an errno and a return value. The errno is + * cleared before the syscall is called and saved to a TST_ERR global variable + * right after the call returns. The return value is available in TST_RET + * global variable. + * + * The TST_ERR and TST_RET can be included into tst_res() messages with the + * TST_ERR and TTERRNO and TRERRNO flags. + * + * This macro is also used as a base for all the more specific variants e.g. + * TST_EXP_FAIL(). + */ #define TEST(SCALL) \ do { \ errno = 0; \ @@ -14,19 +41,16 @@ TST_ERR = errno; \ } while (0) -#define TEST_VOID(SCALL) \ - do { \ - errno = 0; \ - SCALL; \ - TST_ERR = errno; \ - } while (0) - -extern long TST_RET; -extern int TST_ERR; -extern int TST_PASS; - -extern void *TST_RET_PTR; - +/** + * TESTPTR() - Store syscall retval pointer and errno. + * + * @SCALL: Tested syscall e.g. write(fd, buf, len). + * + * Sets TST_RET_PTR and TST_ERR. + * + * This is the same as TEST() macro the only difference is that the return + * value is a pointer which is stored into TST_RET_PTR instead. + */ #define TESTPTR(SCALL) \ do { \ errno = 0; \ @@ -73,12 +97,26 @@ extern void *TST_RET_PTR; \ } while (0) -#define TST_EXP_POSITIVE_(SCALL, ...) \ +#define TST_EXP_POSITIVE_(SCALL, SSCALL, ...) \ ({ \ - TST_EXP_POSITIVE__(SCALL, #SCALL, ##__VA_ARGS__); \ + TST_EXP_POSITIVE__(SCALL, SSCALL, ##__VA_ARGS__); \ TST_RET; \ }) +/** + * TST_EXP_POSITIVE() - Test syscall, expect return value >= 0. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TEST() macro and additionaly prints pass + * or fail message. Apart from TST_ERR and TST_RET set by the TEST() macro + * TST_PASS global variable is set as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ #define TST_EXP_POSITIVE(SCALL, ...) \ ({ \ TST_EXP_POSITIVE__(SCALL, #SCALL, ##__VA_ARGS__); \ @@ -91,8 +129,26 @@ extern void *TST_RET_PTR; TST_RET; \ }) +/** + * TST_EXP_FD_SILENT() - Test syscall to return a file descriptor, silent variant. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * Unlike TST_EXP_FD() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ #define TST_EXP_FD_SILENT(SCALL, ...) TST_EXP_POSITIVE_(SCALL, #SCALL, ##__VA_ARGS__) +/** + * TST_EXP_FD() - Test syscall to return a file descriptor. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * This is a variant of the TST_EXP_POSSITIVE() for a more specific case that + * the returned value is a file descriptor. + */ #define TST_EXP_FD(SCALL, ...) \ ({ \ TST_EXP_POSITIVE__(SCALL, #SCALL, ##__VA_ARGS__); \ @@ -104,6 +160,19 @@ extern void *TST_RET_PTR; TST_RET; \ }) +/** + * TST_EXP_FD_OR_FAIL() - Test syscall to return file descriptor or fail with + * expected errno. + * + * @SCALL: Tested syscall. + * @ERRNO: Expected errno or 0. + * @...: A printf-like parameters. + * + * Expect a file descriptor if errno is 0 otherwise expect a failure with + * expected errno. + * + * Internally it uses TST_EXP_FAIL() and TST_EXP_FD(). + */ #define TST_EXP_FD_OR_FAIL(SCALL, ERRNO, ...) \ ({ \ if (ERRNO) \ @@ -114,8 +183,26 @@ extern void *TST_RET_PTR; TST_RET; \ }) +/** + * TST_EXP_PID_SILENT() - Test syscall to return PID, silent variant. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * Unlike TST_EXP_PID() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ #define TST_EXP_PID_SILENT(SCALL, ...) TST_EXP_POSITIVE_(SCALL, #SCALL, ##__VA_ARGS__) +/** + * TST_EXP_PID() - Test syscall to return PID. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * This is a variant of the TST_EXP_POSSITIVE() for a more specific case that + * the returned value is a pid. + */ #define TST_EXP_PID(SCALL, ...) \ ({ \ TST_EXP_POSITIVE__(SCALL, #SCALL, ##__VA_ARGS__); \ @@ -143,8 +230,34 @@ extern void *TST_RET_PTR; \ } while (0) +/** + * TST_EXP_VAL_SILENT() - Test syscall to return specified value, silent variant. + * + * @SCALL: Tested syscall. + * @VAL: Expected return value. + * @...: A printf-like parameters. + * + * Unlike TST_EXP_VAL() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ #define TST_EXP_VAL_SILENT(SCALL, VAL, ...) TST_EXP_VAL_SILENT_(SCALL, VAL, #SCALL, ##__VA_ARGS__) +/** + * TST_EXP_VAL() - Test syscall to return specified value. + * + * @SCALL: Tested syscall. + * @VAL: Expected return value. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TEST() macro and additionaly prints pass + * or fail message after comparing the returned value againts the expected + * value. Apart from TST_ERR and TST_RET set by the TEST() macro TST_PASS + * global variable is set as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ #define TST_EXP_VAL(SCALL, VAL, ...) \ do { \ TST_EXP_VAL_SILENT_(SCALL, VAL, #SCALL, ##__VA_ARGS__); \ @@ -176,8 +289,54 @@ extern void *TST_RET_PTR; \ } while (0) +#define TST_EXP_PASS_SILENT_PTR_(SCALL, SSCALL, FAIL_PTR_VAL, ...) \ + do { \ + TESTPTR(SCALL); \ + \ + TST_PASS = 0; \ + \ + if (TST_RET_PTR == FAIL_PTR_VAL) { \ + TST_MSG_(TFAIL | TTERRNO, " failed", \ + SSCALL, ##__VA_ARGS__); \ + break; \ + } \ + \ + if (TST_RET != 0) { \ + TST_MSGP_(TFAIL | TTERRNO, " invalid retval %ld", \ + TST_RET, SSCALL, ##__VA_ARGS__); \ + break; \ + } \ + \ + TST_PASS = 1; \ + \ + } while (0) + +/** + * TST_EXP_PASS_SILENT() - Test syscall to return 0, silent variant. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * Unlike TST_EXP_PASS() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ #define TST_EXP_PASS_SILENT(SCALL, ...) TST_EXP_PASS_SILENT_(SCALL, #SCALL, ##__VA_ARGS__) +/** + * TST_EXP_PASS() - Test syscall to return 0. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TEST() macro and additionaly prints pass + * or fail message after checking the return value againts zero. Apart from + * TST_ERR and TST_RET set by the TEST() macro TST_PASS global variable is set + * as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ #define TST_EXP_PASS(SCALL, ...) \ do { \ TST_EXP_PASS_SILENT_(SCALL, #SCALL, ##__VA_ARGS__); \ @@ -186,7 +345,45 @@ extern void *TST_RET_PTR; TST_MSG_(TPASS, " passed", #SCALL, ##__VA_ARGS__); \ } while (0) \ -#define TST_EXP_FAIL_SILENT_(PASS_COND, SCALL, SSCALL, ERRNO, ...) \ +#define TST_EXP_PASS_PTR_(SCALL, SSCALL, FAIL_PTR_VAL, ...) \ + do { \ + TST_EXP_PASS_SILENT_PTR_(SCALL, SSCALL, \ + FAIL_PTR_VAL, ##__VA_ARGS__); \ + \ + if (TST_PASS) \ + TST_MSG_(TPASS, " passed", #SCALL, ##__VA_ARGS__); \ + } while (0) + +/** + * TST_EXP_PASS_PTR_VOID() - Test syscall to return a valid pointer. + * + * @SCALL: Tested syscall. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TESTPTR() macro and additionaly prints + * pass or fail message after checking the return value against (void *)-1. + * Apart from TST_ERR and TST_RET_PTR set by the TESTPTR() macro TST_PASS + * global variable is set as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ +#define TST_EXP_PASS_PTR_VOID(SCALL, ...) \ + TST_EXP_PASS_PTR_(SCALL, #SCALL, (void *)-1, ##__VA_ARGS__); + +/* + * Returns true if err is in the exp_err array. + */ +bool tst_errno_in_set(int err, const int *exp_errs, int exp_errs_cnt); + +/* + * Fills in the buf with the errno names in the exp_err set. The buf must be at + * least 20 * exp_errs_cnt bytes long. + */ +const char *tst_errno_names(char *buf, const int *exp_errs, int exp_errs_cnt); + +#define TST_EXP_FAIL_SILENT_(PASS_COND, SCALL, SSCALL, ERRNOS, ERRNOS_CNT, ...)\ do { \ TEST(SCALL); \ \ @@ -203,63 +400,472 @@ extern void *TST_RET_PTR; break; \ } \ \ - if (TST_ERR == (ERRNO)) { \ + if (tst_errno_in_set(TST_ERR, ERRNOS, ERRNOS_CNT)) { \ TST_PASS = 1; \ } else { \ + char tst_str_buf__[ERRNOS_CNT * 20]; \ TST_MSGP_(TFAIL | TTERRNO, " expected %s", \ - tst_strerrno(ERRNO), \ + tst_errno_names(tst_str_buf__, \ + ERRNOS, ERRNOS_CNT), \ SSCALL, ##__VA_ARGS__); \ } \ } while (0) -#define TST_EXP_FAIL(SCALL, ERRNO, ...) \ +#define TST_EXP_FAIL_SILENT_PTR_(SCALL, SSCALL, FAIL_PTR_VAL, \ + ERRNOS, ERRNOS_CNT, ...) \ do { \ + TESTPTR(SCALL); \ + \ + TST_PASS = 0; \ + \ + if (TST_RET_PTR != FAIL_PTR_VAL) { \ + TST_MSG_(TFAIL, " succeeded", SSCALL, ##__VA_ARGS__); \ + break; \ + } \ + \ + if (!tst_errno_in_set(TST_ERR, ERRNOS, ERRNOS_CNT)) { \ + char tst_str_buf__[ERRNOS_CNT * 20]; \ + TST_MSGP_(TFAIL | TTERRNO, " expected %s", \ + tst_errno_names(tst_str_buf__, \ + ERRNOS, ERRNOS_CNT), \ + SSCALL, ##__VA_ARGS__); \ + break; \ + } \ + \ + TST_PASS = 1; \ + \ + } while (0) + +#define TST_EXP_FAIL_PTR_(SCALL, SSCALL, FAIL_PTR_VAL, \ + ERRNOS, ERRNOS_CNT, ...) \ + do { \ + TST_EXP_FAIL_SILENT_PTR_(SCALL, SSCALL, FAIL_PTR_VAL, \ + ERRNOS, ERRNOS_CNT, ##__VA_ARGS__); \ + if (TST_PASS) \ + TST_MSG_(TPASS | TTERRNO, " ", SSCALL, ##__VA_ARGS__); \ + } while (0) + + +#define TST_EXP_FAIL_ARR_(SCALL, SSCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \ + do { \ + TST_EXP_FAIL_SILENT_(TST_RET == 0, SCALL, SSCALL, \ + EXP_ERRS, EXP_ERRS_CNT, ##__VA_ARGS__); \ + if (TST_PASS) \ + TST_MSG_(TPASS | TTERRNO, " ", SSCALL, ##__VA_ARGS__); \ + } while (0) + +/** + * TST_EXP_FAIL() - Test syscall to fail with expected errno. + * + * @SCALL: Tested syscall. + * @EXP_ERR: Expected errno. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TEST() macro and additionaly prints pass + * or fail message. The check passes if syscall has returned -1 and failed with + * the specified errno. + * + * The SCALL is supposed to return zero on success. For syscalls that return + * positive number on success TST_EXP_FAIL2() has to be used instead. + * + * Apart from TST_ERR and TST_RET set by the TEST() macro TST_PASS global + * variable is set as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ +#define TST_EXP_FAIL(SCALL, EXP_ERR, ...) \ + do { \ + int tst_exp_err__ = EXP_ERR; \ + TST_EXP_FAIL_ARR_(SCALL, #SCALL, &tst_exp_err__, 1, \ + ##__VA_ARGS__); \ + } while (0) + +/** + * TST_EXP_FAIL_ARR() - Test syscall to fail with expected errnos. + * + * @SCALL: Tested syscall. + * @EXP_ERRS: Array of expected errnos. + * @EXP_ERRS_CNT: Lenght of EXP_ERRS. + * @...: A printf-like parameters. + * + * This is a variant of TST_EXP_FAIL() with an array of possible errors. + */ +#define TST_EXP_FAIL_ARR(SCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \ + TST_EXP_FAIL_ARR_(SCALL, #SCALL, EXP_ERRS, \ + EXP_ERRS_CNT, ##__VA_ARGS__); + +#define TST_EXP_FAIL2_ARR_(SCALL, SSCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \ + do { \ + TST_EXP_FAIL_SILENT_(TST_RET >= 0, SCALL, SSCALL, \ + EXP_ERRS, EXP_ERRS_CNT, ##__VA_ARGS__); \ + if (TST_PASS) \ + TST_MSG_(TPASS | TTERRNO, " ", SSCALL, ##__VA_ARGS__); \ + } while (0) + +/** + * TST_EXP_FAIL2_ARR() - Test syscall to fail with expected errnos. + * + * @SCALL: Tested syscall. + * @EXP_ERRS: Array of expected errnos. + * @EXP_ERRS_CNT: Lenght of EXP_ERRS. + * @...: A printf-like parameters. + * + * This is a variant of TST_EXP_FAIL2() with an array of possible errors. + */ +#define TST_EXP_FAIL2_ARR(SCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \ + TST_EXP_FAIL2_ARR_(SCALL, #SCALL, EXP_ERRS, \ + EXP_ERRS_CNT, ##__VA_ARGS__); + +/** + * TST_EXP_FAIL_PTR_NULL() - Test syscall to fail with expected errno and return a NULL pointer. + * + * @SCALL: Tested syscall. + * @EXP_ERR: Expected errno. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TESTPTR() macro and additionaly prints + * pass or fail message after checking the return value against NULL and errno. + * + * Apart from TST_ERR and TST_RET_PTR set by the TESTPTR() macro TST_PASS + * global variable is set as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ +#define TST_EXP_FAIL_PTR_NULL(SCALL, EXP_ERR, ...) \ + do { \ + int tst_exp_err__ = EXP_ERR; \ + TST_EXP_FAIL_PTR_(SCALL, #SCALL, NULL, \ + &tst_exp_err__, 1, ##__VA_ARGS__); \ + } while (0) + +/** + * TST_EXP_FAIL_PTR_NULL_ARR() - Test syscall to fail with expected errnos and return a NULL pointer. + * + * @SCALL: Tested syscall. + * @EXP_ERRS: Array of expected errnos. + * @EXP_ERRS_CNT: Lenght of EXP_ERRS. + * @...: A printf-like parameters. + * + * This is a variant of TST_EXP_FAIL_PTR_NULL() with an array of possible + * errors. + */ +#define TST_EXP_FAIL_PTR_NULL_ARR(SCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \ + TST_EXP_FAIL_PTR_(SCALL, #SCALL, NULL, \ + EXP_ERRS, EXP_ERRS_CNT, ##__VA_ARGS__); + + + +/** + * TST_EXP_FAIL_PTR_VOID() - Test syscall to fail with expected errno and return a (void *)-1 pointer. + * + * @SCALL: Tested syscall. + * @EXP_ERR: Expected errno. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TESTPTR() macro and additionaly prints + * pass or fail message after checking the return value against (void *)-1 and + * errno. + * + * Apart from TST_ERR and TST_RET_PTR set by the TESTPTR() macro TST_PASS + * global variable is set as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ +#define TST_EXP_FAIL_PTR_VOID(SCALL, EXP_ERR, ...) \ + do { \ + int tst_exp_err__ = EXP_ERR; \ + TST_EXP_FAIL_PTR_(SCALL, #SCALL, (void *)-1, \ + &tst_exp_err__, 1, ##__VA_ARGS__); \ + } while (0) + +/** + * TST_EXP_FAIL_PTR_VOID_ARR() - Test syscall to fail with expected errnos and return a (void *)-1 pointer. + * + * @SCALL: Tested syscall. + * @EXP_ERRS: Array of expected errnos. + * @EXP_ERRS_CNT: Lenght of EXP_ERRS. + * @...: A printf-like parameters. + * + * This is a variant of TST_EXP_FAIL_PTR_VOID() with an array of possible + * errors. + */ +#define TST_EXP_FAIL_PTR_VOID_ARR(SCALL, EXP_ERRS, EXP_ERRS_CNT, ...) \ + TST_EXP_FAIL_PTR_(SCALL, #SCALL, (void *)-1, \ + EXP_ERRS, EXP_ERRS_CNT, ##__VA_ARGS__); + +/** + * TST_EXP_FAIL2() - Test syscall to fail with expected errno. + * + * @SCALL: Tested syscall. + * @EXP_ERR: Expected errno. + * @...: A printf-like parameters. + * + * This macro calls the SCALL with a TEST() macro and additionaly prints pass + * or fail message. The check passes if syscall has returned -1 and failed with + * the specified errno. + * + * The SCALL is supposed to return possitive number on success e.g. pid or file + * descriptor. For syscalls that return zero on success TST_EXP_FAIL() has to + * be used instead. + * + * Apart from TST_ERR and TST_RET set by the TEST() macro TST_PASS global + * variable is set as well based on the outcome. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the first parameter + * is converted to a string and used instead. + */ +#define TST_EXP_FAIL2(SCALL, EXP_ERR, ...) \ + do { \ + int tst_exp_err__ = EXP_ERR; \ + TST_EXP_FAIL2_ARR_(SCALL, #SCALL, &tst_exp_err__, 1, \ + ##__VA_ARGS__); \ + } while (0) + +/** + * TST_EXP_FAIL_SILENT() - Test syscall to fail with expected errno, silent variant. + * + * @SCALL: Tested syscall. + * @EXP_ERR: Expected errno. + * @...: A printf-like parameters. + * + * Unlike TST_EXP_FAIL() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ +#define TST_EXP_FAIL_SILENT(SCALL, EXP_ERR, ...) \ + do { \ + int tst_exp_err__ = EXP_ERR; \ TST_EXP_FAIL_SILENT_(TST_RET == 0, SCALL, #SCALL, \ - ERRNO, ##__VA_ARGS__); \ - if (TST_PASS) \ - TST_MSG_(TPASS | TTERRNO, " ", #SCALL, ##__VA_ARGS__); \ + &tst_exp_err__, 1, ##__VA_ARGS__); \ } while (0) -#define TST_EXP_FAIL2(SCALL, ERRNO, ...) \ +/** + * TST_EXP_FAIL2_SILENT() - Test syscall to fail with expected errno, silent variant. + * + * @SCALL: Tested syscall. + * @EXP_ERR: Expected errno. + * @...: A printf-like parameters. + * + * Unlike TST_EXP_FAIL2() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ +#define TST_EXP_FAIL2_SILENT(SCALL, EXP_ERR, ...) \ do { \ + int tst_exp_err__ = EXP_ERR; \ TST_EXP_FAIL_SILENT_(TST_RET >= 0, SCALL, #SCALL, \ - ERRNO, ##__VA_ARGS__); \ - if (TST_PASS) \ - TST_MSG_(TPASS | TTERRNO, " ", #SCALL, ##__VA_ARGS__); \ + &tst_exp_err__, 1, ##__VA_ARGS__); \ } while (0) -#define TST_EXP_FAIL_SILENT(SCALL, ERRNO, ...) \ - TST_EXP_FAIL_SILENT_(TST_RET == 0, SCALL, #SCALL, ERRNO, ##__VA_ARGS__) +/** + * TST_EXP_EXPR() - Check for expected expression. + * + * @EXPR: Expression to be tested. + * @...: A printf-like parameters. + * + * Reports a pass if expression evaluates to non-zero and a fail otherwise. + * + * The printf-like parameters can be optionally used to pass a description + * printed by the pass or fail tst_res() calls. If omitted the expression + * is converted to a string and used instead. + */ +#define TST_EXP_EXPR(EXPR, ...) \ + tst_res_(__FILE__, __LINE__, (EXPR) ? TPASS : TFAIL, "Expect: " \ + TST_FMT_(TST_2_(dummy, ##__VA_ARGS__, #EXPR), __VA_ARGS__)); -#define TST_EXP_FAIL2_SILENT(SCALL, ERRNO, ...) \ - TST_EXP_FAIL_SILENT_(TST_RET >= 0, SCALL, #SCALL, ERRNO, ##__VA_ARGS__) - -#define TST_EXP_EXPR(EXPR, FMT, ...) \ - tst_res_(__FILE__, __LINE__, (EXPR) ? TPASS : TFAIL, "Expect: " FMT, ##__VA_ARGS__); - -#define TST_EXP_EQ_(VAL_A, SVAL_A, VAL_B, SVAL_B, TYPE, PFS) do {\ - TYPE tst_tmp_a__ = VAL_A; \ - TYPE tst_tmp_b__ = VAL_B; \ - if (tst_tmp_a__ == tst_tmp_b__) { \ - tst_res_(__FILE__, __LINE__, TPASS, \ - SVAL_A " == " SVAL_B " (" PFS ")", tst_tmp_a__); \ - } else { \ - tst_res_(__FILE__, __LINE__, TFAIL, \ - SVAL_A " (" PFS ") != " SVAL_B " (" PFS ")", \ - tst_tmp_a__, tst_tmp_b__); \ - } \ +#define TST_EXP_EQ_SILENT_(VAL_A, SVAL_A, VAL_B, SVAL_B, TYPE, PFS) do { \ + TYPE tst_tmp_a__ = VAL_A; \ + TYPE tst_tmp_b__ = VAL_B; \ + \ + TST_PASS = 0; \ + \ + if (tst_tmp_a__ != tst_tmp_b__) { \ + tst_res_(__FILE__, __LINE__, TFAIL, \ + SVAL_A " (" PFS ") != " SVAL_B " (" PFS ")", \ + tst_tmp_a__, tst_tmp_b__); \ + } else { \ + TST_PASS = 1; \ + } \ } while (0) -#define TST_EXP_EQ_LI(VAL_A, VAL_B) \ - TST_EXP_EQ_(VAL_A, #VAL_A, VAL_B, #VAL_B, long long, "%lli") +/** + * TST_EXP_EQ_LI() - Compare two long long values. + * + * @VAL_A: long long value A. + * @VAL_B: long long value B. + * + * Reports a pass if values are equal and a fail otherwise. + */ +#define TST_EXP_EQ_LI(VAL_A, VAL_B) do { \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, long long, "%lli"); \ + \ + if (TST_PASS) { \ + tst_res_(__FILE__, __LINE__, TPASS, \ + #VAL_A " == " #VAL_B " (%lli)", \ + (long long)VAL_A); \ + } \ +} while (0) -#define TST_EXP_EQ_LU(VAL_A, VAL_B) \ - TST_EXP_EQ_(VAL_A, #VAL_A, VAL_B, #VAL_B, unsigned long long, "%llu") +/** + * TST_EXP_EQ_LI_SILENT() - Compare two long long values, silent variant. + * + * @VAL_A: long long value A. + * @VAL_B: long long value B. + * + * Unlike TST_EXP_EQ_LI() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ +#define TST_EXP_EQ_LI_SILENT(VAL_A, VAL_B) \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, long long, "%lli") -#define TST_EXP_EQ_SZ(VAL_A, VAL_B) \ - TST_EXP_EQ_(VAL_A, #VAL_A, VAL_B, #VAL_B, size_t, "%zu") +/** + * TST_EXP_EQ_LU() - Compare two unsigned long long values. + * + * @VAL_A: unsigned long long value A. + * @VAL_B: unsigned long long value B. + * + * Reports a pass if values are equal and a fail otherwise. + */ +#define TST_EXP_EQ_LU(VAL_A, VAL_B) do { \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, unsigned long long, "%llu"); \ + \ + if (TST_PASS) { \ + tst_res_(__FILE__, __LINE__, TPASS, \ + #VAL_A " == " #VAL_B " (%llu)", \ + (unsigned long long)VAL_A); \ + } \ +} while (0) -#define TST_EXP_EQ_SSZ(VAL_A, VAL_B) \ - TST_EXP_EQ_(VAL_A, #VAL_A, VAL_B, #VAL_B, ssize_t, "%zi") +/** + * TST_EXP_EQ_LU_SILENT() - Compare two unsigned long long values, silent variant. + * + * @VAL_A: unsigned long long value A. + * @VAL_B: unsigned long long value B. + * + * Unlike TST_EXP_EQ_LU() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ +#define TST_EXP_EQ_LU_SILENT(VAL_A, VAL_B) \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, unsigned long long, "%llu") + +/** + * TST_EXP_EQ_SZ() - Compare two unsigned size_t values. + * + * @VAL_A: unsigned long long value A. + * @VAL_B: unsigned long long value B. + * + * Reports a pass if values are equal and a fail otherwise. + */ +#define TST_EXP_EQ_SZ(VAL_A, VAL_B) do { \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, size_t, "%zu"); \ + \ + if (TST_PASS) { \ + tst_res_(__FILE__, __LINE__, TPASS, \ + #VAL_A " == " #VAL_B " (%zu)", \ + (size_t)VAL_A); \ + } \ +} while (0) + +/** + * TST_EXP_EQ_SZ_SILENT() - Compare two unsigned size_t values, silent variant. + * + * @VAL_A: unsigned long long value A. + * @VAL_B: unsigned long long value B. + * + * Unlike TST_EXP_EQ_SZ() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ +#define TST_EXP_EQ_SZ_SILENT(VAL_A, VAL_B) \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, size_t, "%zu") + +/** + * TST_EXP_EQ_SSZ() - Compare two unsigned ssize_t values. + * + * @VAL_A: unsigned long long value A. + * @VAL_B: unsigned long long value B. + * + * Reports a pass if values are equal and a fail otherwise. + */ +#define TST_EXP_EQ_SSZ(VAL_A, VAL_B) do { \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, size_t, "%zi"); \ + \ + if (TST_PASS) { \ + tst_res_(__FILE__, __LINE__, TPASS, \ + #VAL_A " == " #VAL_B " (%zi)", \ + (ssize_t)VAL_A); \ + } \ +} while (0) + +/** + * TST_EXP_EQ_SSZ_SILENT() - Compare two unsigned ssize_t values, silent variant. + * + * @VAL_A: unsigned long long value A. + * @VAL_B: unsigned long long value B. + * + * Unlike TST_EXP_EQ_SSZ() does not print :c:enum:`TPASS ` on + * success, only prints :c:enum:`TFAIL ` on failure. + */ +#define TST_EXP_EQ_SSZ_SILENT(VAL_A, VAL_B) \ + TST_EXP_EQ_SILENT_(VAL_A, #VAL_A, VAL_B, #VAL_B, ssize_t, "%zi") + +/** + * TST_EXP_EQ_STR() - Compare two strings. + * + * @STR_A: string to compare. + * @STR_B: string to compare. + * + * Reports a pass if strings are equal and a fail otherwise. + */ +#define TST_EXP_EQ_STR(STR_A, STR_B) do { \ + TST_PASS = strcmp(STR_A, STR_B) == 0; \ + \ + if (TST_PASS) { \ + tst_res_(__FILE__, __LINE__, TPASS, \ + "%s == %s (%s)", \ + #STR_A, #STR_B, STR_B); \ + } else { \ + tst_res_(__FILE__, __LINE__, TFAIL, \ + "%s (%s) != %s (%s)", \ + #STR_A, STR_A, #STR_B, STR_B); \ + } \ +} while (0) + +/** + * TST_EXP_EQ_STRN() - Compare two strings, providing length as well. + * + * @STR_A: string to compare. + * @STR_B: string to compare. + * @LEN: length of the string. + * + * Reports a pass if first LEN bytes of strings are equal and a fail otherwise. + */ +#define TST_EXP_EQ_STRN(STR_A, STR_B, LEN) do { \ + char str_a_cpy[LEN+1]; \ + \ + strncpy(str_a_cpy, STR_A, LEN); \ + str_a_cpy[LEN] = 0; \ + \ + TST_PASS = strncmp(STR_A, STR_B, LEN) == 0; \ + \ + if (TST_PASS) { \ + tst_res_(__FILE__, __LINE__, TPASS, \ + "%s == %s (%s)", \ + #STR_A, #STR_B, str_a_cpy); \ + } else { \ + char str_b_cpy[LEN+1]; \ + \ + strncpy(str_b_cpy, STR_B, LEN); \ + str_b_cpy[LEN] = 0; \ + \ + tst_res_(__FILE__, __LINE__, TFAIL, \ + "%s (%s) != %s (%s)", \ + #STR_A, str_a_cpy, #STR_B, str_b_cpy); \ + } \ +} while (0) #endif /* TST_TEST_MACROS_H__ */ diff --git a/include/tst_timer.h b/include/tst_timer.h index 703f0329..6fb94002 100755 --- a/include/tst_timer.h +++ b/include/tst_timer.h @@ -135,6 +135,13 @@ struct __kernel_itimerspec { struct __kernel_timespec it_value; /* timer expiration */ }; #endif + +#ifndef HAVE_STRUCT___KERNEL_OLD_ITIMERVAL +struct __kernel_old_itimerval { + struct __kernel_old_timeval it_interval; /* timer interval */ + struct __kernel_old_timeval it_value; /* current value */ +}; +#endif #endif enum tst_ts_type { @@ -370,6 +377,11 @@ static inline int sys_timerfd_settime64(int fd, int flags, void *its, return tst_syscall(__NR_timerfd_settime64, fd, flags, its, old_its); } +static inline int sys_setitimer(int which, void *new_value, void *old_value) +{ + return tst_syscall(__NR_setitimer, which, new_value, old_value); +} + /* * Returns tst_ts seconds. */ diff --git a/include/tst_tmpdir.h b/include/tst_tmpdir.h new file mode 100644 index 00000000..85d70823 --- /dev/null +++ b/include/tst_tmpdir.h @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2017-2024 Cyril Hrubis + * Copyright (c) 2020 Martin Doucha + */ + +#ifndef TST_TMPDIR_H__ +#define TST_TMPDIR_H__ + +/** + * tst_purge_dir - Wipe the content of given directory. + * + * Wipe the content of given directory but keep the directory itself. + * + * @path: Path of the directory to be wiped. + */ +void tst_purge_dir(const char *path); + +/** + * tst_tmpdir_path - Returns a pointer to a tmpdir path. + * + * The returned path is allocated and initialized the first time this function is + * called, each subsequent call will return the same pointer. + * + * return: A newly allocated path. The memory is freed automatically at the end + * of the test. If allocation fails the function calls tst_brk() and + * exits the test. + */ +char *tst_tmpdir_path(void); + +/** + * tst_tmpdir_genpath - Construct an absolute path pointing to a file inside tmpdir. + * + * Constructs a path inside tmpdir i.e. adds a prefix pointing to the current + * test tmpdir to the string build by the printf-like format. + * + * @fmt: A printf-like format string. + * @...: A printf-like parameter list. + * + * return: A newly allocated path. The memory is freed automatically at the end + * of the test. If allocation fails the function calls tst_brk() and exits the + * test. + */ +char *tst_tmpdir_genpath(const char *fmt, ...) + __attribute__((format(printf, 1, 2))); + +/* + * Make sure nobody uses old API functions in new code. + */ +#ifndef LTPLIB +# define tst_get_tmpdir #error Use tst_tmpdir_path()! +#endif + +#endif /* TST_TMPDIR_H__ */ diff --git a/include/ujson.h b/include/ujson.h new file mode 100644 index 00000000..8faeb18f --- /dev/null +++ b/include/ujson.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2021-2024 Cyril Hrubis + */ + +#ifndef UJSON_H +#define UJSON_H + +#include +#include +#include + +#endif /* UJSON_H */ diff --git a/include/ujson_common.h b/include/ujson_common.h new file mode 100644 index 00000000..ed31c090 --- /dev/null +++ b/include/ujson_common.h @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2021-2024 Cyril Hrubis + */ + +/** + * @file ujson_common.h + * @brief Common JSON reader/writer definitions. + */ + +#ifndef UJSON_COMMON_H +#define UJSON_COMMON_H + +/** @brief Maximal error message length. */ +#define UJSON_ERR_MAX 128 +/** @brief Maximal id string lenght including terminating null element. */ +#define UJSON_ID_MAX 64 +/** @brief Maximal recursion depth allowed. */ +#define UJSON_RECURSION_MAX 128 + +#define UJSON_ERR_PRINT ujson_err_handler +#define UJSON_ERR_PRINT_PRIV stderr + +/** + * @brief A JSON data type. + */ +enum ujson_type { + /** @brief No type. Returned when parser finishes. */ + UJSON_VOID = 0, + /** @brief An integer. */ + UJSON_INT, + /** @brief A floating point. */ + UJSON_FLOAT, + /** @brief A boolean. */ + UJSON_BOOL, + /** @brief NULL */ + UJSON_NULL, + /** @brief A string. */ + UJSON_STR, + /** @brief A JSON object. */ + UJSON_OBJ, + /** @brief A JSON array. */ + UJSON_ARR, +}; + +/** + * @brief Returns type name. + * + * @param type A json type. + * @return A type name. + */ +const char *ujson_type_name(enum ujson_type type); + +/** + * @brief Default error print handler. + * + * @param print_priv A json buffer print_priv pointer. + * @param line A line of text to be printed. + */ +void ujson_err_handler(void *print_priv, const char *line); + +typedef struct ujson_reader ujson_reader; +typedef struct ujson_writer ujson_writer; +typedef struct ujson_val ujson_val; + +/** @brief An array size macro. */ +#define UJSON_ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) + +#endif /* UJSON_COMMON_H */ diff --git a/include/ujson_reader.h b/include/ujson_reader.h new file mode 100644 index 00000000..273fe624 --- /dev/null +++ b/include/ujson_reader.h @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2021-2024 Cyril Hrubis + */ + +/** + * @file ujson_reader.h + * @brief A recursive descend JSON parser. + * + * All the function that parse JSON return zero on success and non-zero on a + * failure. Once an error has happened all subsequent attempts to parse more + * return with non-zero exit status immediatelly. This is designed so that we + * can parse several values without checking each return value and only check + * if error has happened at the end of the sequence. + */ + +#ifndef UJSON_READER_H +#define UJSON_READER_H + +#include +#include + +/** + * @brief An ujson_reader initializer with default values. + * + * @param buf A pointer to a buffer with JSON data. + * @param buf_len A JSON data buffer lenght. + * @param rflags enum ujson_reader_flags. + * + * @return An ujson_reader initialized with default values. + */ +#define UJSON_READER_INIT(buf, buf_len, rflags) { \ + .max_depth = UJSON_RECURSION_MAX, \ + .err_print = UJSON_ERR_PRINT, \ + .err_print_priv = UJSON_ERR_PRINT_PRIV, \ + .json = buf, \ + .len = buf_len, \ + .flags = rflags \ +} + +/** @brief Reader flags. */ +enum ujson_reader_flags { + /** @brief If set warnings are treated as errors. */ + UJSON_READER_STRICT = 0x01, +}; + +/** + * @brief A JSON parser internal state. + */ +struct ujson_reader { + /** Pointer to a null terminated JSON string */ + const char *json; + /** A length of the JSON string */ + size_t len; + /** A current offset into the JSON string */ + size_t off; + /** An offset to the start of the last array or object */ + size_t sub_off; + /** Recursion depth increased when array/object is entered decreased on leave */ + unsigned int depth; + /** Maximal recursion depth */ + unsigned int max_depth; + + /** Reader flags. */ + enum ujson_reader_flags flags; + + /** Handler to print errors and warnings */ + void (*err_print)(void *err_print_priv, const char *line); + void *err_print_priv; + + char err[UJSON_ERR_MAX]; + char buf[]; +}; + +/** + * @brief An ujson_val initializer. + * + * @param sbuf A pointer to a buffer used for string values. + * @param sbuf_size A length of the buffer used for string values. + * + * @return An ujson_val initialized with default values. + */ +#define UJSON_VAL_INIT(sbuf, sbuf_size) { \ + .buf = sbuf, \ + .buf_size = sbuf_size, \ +} + +/** + * @brief A parsed JSON key value pair. + */ +struct ujson_val { + /** + * @brief A value type + * + * UJSON_VALUE_VOID means that no value was parsed. + */ + enum ujson_type type; + + /** An user supplied buffer and size to store a string values to. */ + char *buf; + size_t buf_size; + + /** + * @brief An index to attribute list. + * + * This is set by the ujson_obj_first_filter() and + * ujson_obj_next_filter() functions. + */ + size_t idx; + + /** An union to store the parsed value into. */ + union { + /** @brief A boolean value. */ + int val_bool; + /** @brief An integer value. */ + long long val_int; + /** @brief A string value. */ + const char *val_str; + }; + + /** + * @brief A floating point value. + * + * Since integer values are subset of floating point values val_float + * is always set when val_int was set. + */ + double val_float; + + /** @brief An ID for object values */ + char id[UJSON_ID_MAX]; + + char buf__[]; +}; + +/** + * @brief Allocates a JSON value. + * + * @param buf_size A maximal buffer size for a string value, pass 0 for default. + * @return A newly allocated JSON value. + */ +ujson_val *ujson_val_alloc(size_t buf_size); + +/** + * @brief Frees a JSON value. + * + * @param self A JSON value previously allocated by ujson_val_alloc(). + */ +void ujson_val_free(ujson_val *self); + +/** + * @brief Checks is result has valid type. + * + * @param res An ujson value. + * @return Zero if result is not valid, non-zero otherwise. + */ +static inline int ujson_val_valid(struct ujson_val *res) +{ + return !!res->type; +} + +/** + * @brief Fills the reader error. + * + * Once buffer error is set all parsing functions return immediatelly with type + * set to UJSON_VOID. + * + * @param self An ujson_reader + * @param fmt A printf like format string + * @param ... A printf like parameters + */ +void ujson_err(ujson_reader *self, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +/** + * @brief Prints error stored in the buffer. + * + * The error takes into consideration the current offset in the buffer and + * prints a few preceding lines along with the exact position of the error. + * + * The error is passed to the err_print() handler. + * + * @param self A ujson_reader + */ +void ujson_err_print(ujson_reader *self); + +/** + * @brief Prints a warning. + * + * Uses the print handler in the buffer to print a warning along with a few + * lines of context from the JSON at the current position. + * + * @param self A ujson_reader + * @param fmt A printf-like error string. + * @param ... A printf-like parameters. + */ +void ujson_warn(ujson_reader *self, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +/** + * @brief Returns true if error was encountered. + * + * @param self A ujson_reader + * @return True if error was encountered false otherwise. + */ +static inline int ujson_reader_err(ujson_reader *self) +{ + return !!self->err[0]; +} + +/** + * @brief Returns the type of next element in buffer. + * + * @param self An ujson_reader + * @return A type of next element in the buffer. + */ +enum ujson_type ujson_next_type(ujson_reader *self); + +/** + * @brief Returns if first element in JSON is object or array. + * + * @param self A ujson_reader + * @return On success returns UJSON_OBJ or UJSON_ARR. On failure UJSON_VOID. + */ +enum ujson_type ujson_reader_start(ujson_reader *self); + +/** + * @brief Starts parsing of a JSON object. + * + * @param self An ujson_reader + * @param res An ujson_val to store the parsed value to. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_obj_first(ujson_reader *self, struct ujson_val *res); + +/** + * @brief Parses next value from a JSON object. + * + * If the res->type is UJSON_OBJ or UJSON_ARR it has to be parsed or skipped + * before next call to this function. + * + * @param self An ujson_reader. + * @param res A ujson_val to store the parsed value to. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_obj_next(ujson_reader *self, struct ujson_val *res); + +/** + * @brief A loop over a JSON object. + * + * @code + * UJSON_OBJ_FOREACH(reader, val) { + * printf("Got value id '%s' type '%s'", val->id, ujson_type_name(val->type)); + * ... + * } + * @endcode + * + * @param self An ujson_reader. + * @param res An ujson_val to store the next parsed value to. + */ +#define UJSON_OBJ_FOREACH(self, res) \ + for (ujson_obj_first(self, res); ujson_val_valid(res); ujson_obj_next(self, res)) + +/** + * @brief Utility function for log(n) lookup in a sorted array. + * + * @param list Analphabetically sorted array. + * @param list_len Array length. + * + * @return An array index or (size_t)-1 if key wasn't found. + */ +size_t ujson_lookup(const void *arr, size_t memb_size, size_t list_len, + const char *key); + +/** + * @brief A JSON object attribute description i.e. key and type. + */ +typedef struct ujson_obj_attr { + /** @brief A JSON object key name. */ + const char *key; + /** + * @brief A JSON object value type. + * + * Note that because integer numbers are subset of floating point + * numbers if requested type was UJSON_FLOAT it will match if parsed + * type was UJSON_INT and the val_float will be set in addition to + * val_int. + */ + enum ujson_type type; +} ujson_obj_attr; + +/** @brief A JSON object description */ +typedef struct ujson_obj { + /** + * @brief A list of attributes. + * + * Attributes we are looking for, the parser sets the val->idx for these. + */ + const ujson_obj_attr *attrs; + /** @brief A size of attrs array. */ + size_t attr_cnt; +} ujson_obj; + +static inline size_t ujson_obj_lookup(const ujson_obj *obj, const char *key) +{ + return ujson_lookup(obj->attrs, sizeof(*obj->attrs), obj->attr_cnt, key); +} + +/** @brief An ujson_obj_attr initializer. */ +#define UJSON_OBJ_ATTR(keyv, typev) \ + {.key = keyv, .type = typev} + +/** @brief An ujson_obj_attr intializer with an array index. */ +#define UJSON_OBJ_ATTR_IDX(key_idx, keyv, typev) \ + [key_idx] = {.key = keyv, .type = typev} + +/** + * @brief Starts parsing of a JSON object with attribute lists. + * + * @param self An ujson_reader. + * @param res An ujson_val to store the parsed value to. + * @param obj An ujson_obj object description. + * @param ign A list of keys to ignore. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_obj_first_filter(ujson_reader *self, struct ujson_val *res, + const struct ujson_obj *obj, const struct ujson_obj *ign); + +/** + * @brief An empty object attribute list. + * + * To be passed to UJSON_OBJ_FOREACH_FITLER() as ignore list. + */ +extern const struct ujson_obj *ujson_empty_obj; + +/** + * @brief Parses next value from a JSON object with attribute lists. + * + * If the res->type is UJSON_OBJ or UJSON_ARR it has to be parsed or skipped + * before next call to this function. + * + * @param self An ujson_reader. + * @param res An ujson_val to store the parsed value to. + * @param obj An ujson_obj object description. + * @param ign A list of keys to ignore. If set to NULL all unknown keys are + * ignored, if set to ujson_empty_obj all unknown keys produce warnings. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_obj_next_filter(ujson_reader *self, struct ujson_val *res, + const struct ujson_obj *obj, const struct ujson_obj *ign); + +/** + * @brief A loop over a JSON object with a pre-defined list of expected attributes. + * + * @code + * static struct ujson_obj_attr attrs[] = { + * UJSON_OBJ_ATTR("bool", UJSON_BOOL), + * UJSON_OBJ_ATTR("number", UJSON_INT), + * }; + * + * static struct ujson_obj obj = { + * .attrs = filter_attrs, + * .attr_cnt = UJSON_ARRAY_SIZE(filter_attrs) + * }; + * + * UJSON_OBJ_FOREACH_FILTER(reader, val, &obj, NULL) { + * printf("Got value id '%s' type '%s'", + * attrs[val->idx].id, ujson_type_name(val->type)); + * ... + * } + * @endcode + * + * @param self An ujson_reader. + * @param res An ujson_val to store the next parsed value to. + * @param obj An ujson_obj with a description of attributes to parse. + * @param ign An ujson_obj with a description of attributes to ignore. + */ +#define UJSON_OBJ_FOREACH_FILTER(self, res, obj, ign) \ + for (ujson_obj_first_filter(self, res, obj, ign); \ + ujson_val_valid(res); \ + ujson_obj_next_filter(self, res, obj, ign)) + +/** + * @brief Skips parsing of a JSON object. + * + * @param self An ujson_reader. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_obj_skip(ujson_reader *self); + +/** + * @brief Starts parsing of a JSON array. + * + * @param self An ujson_reader. + * @param res An ujson_val to store the parsed value to. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_arr_first(ujson_reader *self, struct ujson_val *res); + +/** + * @brief Parses next value from a JSON array. + * + * If the res->type is UJSON_OBJ or UJSON_ARR it has to be parsed or skipped + * before next call to this function. + * + * @param self An ujson_reader. + * @param res An ujson_val to store the parsed value to. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_arr_next(ujson_reader *self, struct ujson_val *res); + +/** + * @brief A loop over a JSON array. + * + * @code + * UJSON_ARR_FOREACH(reader, val) { + * printf("Got value type '%s'", ujson_type_name(val->type)); + * ... + * } + * @endcode + * + * @param self An ujson_reader. + * @param res An ujson_val to store the next parsed value to. + */ +#define UJSON_ARR_FOREACH(self, res) \ + for (ujson_arr_first(self, res); ujson_val_valid(res); ujson_arr_next(self, res)) + +/** + * @brief Skips parsing of a JSON array. + * + * @param self A ujson_reader. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_arr_skip(ujson_reader *self); + +/** + * @brief A JSON reader state. + */ +typedef struct ujson_reader_state { + size_t off; + unsigned int depth; +} ujson_reader_state; + +/** + * @brief Returns a parser state at the start of current object/array. + * + * This function could be used for the parser to return to the start of the + * currently parsed object or array. + * + * @param self A ujson_reader + * @return A state that points to a start of the last object or array. + */ +static inline ujson_reader_state ujson_reader_state_save(ujson_reader *self) +{ + struct ujson_reader_state ret = { + .off = self->sub_off, + .depth = self->depth, + }; + + return ret; +} + +/** + * @brief Returns the parser to a saved state. + * + * This function could be used for the parser to return to the start of + * object or array saved by t the ujson_reader_state_get() function. + * + * @param self A ujson_reader + * @param state An parser state as returned by the ujson_reader_state_get(). + */ +static inline void ujson_reader_state_load(ujson_reader *self, ujson_reader_state state) +{ + if (ujson_reader_err(self)) + return; + + self->off = state.off; + self->sub_off = state.off; + self->depth = state.depth; +} + +/** + * @brief Resets the parser to a start. + * + * @param self A ujson_reader + */ +static inline void ujson_reader_reset(ujson_reader *self) +{ + self->off = 0; + self->sub_off = 0; + self->depth = 0; + self->err[0] = 0; +} + +/** + * @brief Loads a file into an ujson_reader buffer. + * + * The reader has to be later freed by ujson_reader_free(). + * + * @param path A path to a file. + * @return A ujson_reader or NULL in a case of a failure. + */ +ujson_reader *ujson_reader_load(const char *path); + +/** + * @brief Frees an ujson_reader buffer. + * + * @param self A ujson_reader allocated by ujson_reader_load() function. + */ +void ujson_reader_free(ujson_reader *self); + +/** + * @brief Prints errors and warnings at the end of parsing. + * + * Checks if self->err is set and prints the error with ujson_reader_err() + * + * Checks if there is any text left after the parser has finished with + * ujson_reader_consumed() and prints a warning if there were any non-whitespace + * characters left. + * + * @param self A ujson_reader + */ +void ujson_reader_finish(ujson_reader *self); + +/** + * @brief Returns non-zero if whole buffer has been consumed. + * + * @param self A ujson_reader. + * @return Non-zero if whole buffer was consumed. + */ +static inline int ujson_reader_consumed(ujson_reader *self) +{ + return self->off >= self->len; +} + +#endif /* UJSON_H */ diff --git a/include/ujson_utf.h b/include/ujson_utf.h new file mode 100644 index 00000000..f939fbe8 --- /dev/null +++ b/include/ujson_utf.h @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2022-2024 Cyril Hrubis + */ + +/** + * @file ujson_utf.h + * @brief Unicode helper macros and functions. + */ + +#ifndef UJSON_UTF_H +#define UJSON_UTF_H + +#include +#include + +/** Returns true if unicode byte is ASCII */ +#define UJSON_UTF8_IS_ASCII(ch) (!((ch) & 0x80)) +/** Returns true if we have first unicode byte of single byte sequence */ +#define UJSON_UTF8_IS_NBYTE(ch) (((ch) & 0xc0) == 0x80) +/** Returns true if we have first unicode byte of two byte sequence */ +#define UJSON_UTF8_IS_2BYTE(ch) (((ch) & 0xe0) == 0xc0) +/** Returns true if we have first unicode byte of three byte sequence */ +#define UJSON_UTF8_IS_3BYTE(ch) (((ch) & 0xf0) == 0xe0) +/** Returns true if we have first unicode byte of four byte sequence */ +#define UJSON_UTF8_IS_4BYTE(ch) (((ch) & 0xf8) == 0xf0) + +#define UJSON_UTF8_NBYTE_MASK 0x3f + +/** + * @brief Parses next unicode character in UTF-8 string. + * @param str A pointer to the C string. + * @return A unicode character or 0 on error or end of the string. + */ +static inline uint32_t ujson_utf8_next(const char **str) +{ + uint32_t s0 = *str[0]; + + (*str)++; + + if (UJSON_UTF8_IS_ASCII(s0)) + return s0; + + uint32_t s1 = *str[0]; + + if (!UJSON_UTF8_IS_NBYTE(s1)) + return 0; + + s1 &= UJSON_UTF8_NBYTE_MASK; + + (*str)++; + + if (UJSON_UTF8_IS_2BYTE(s0)) + return (s0 & 0x1f)<<6 | s1; + + uint32_t s2 = *str[0]; + + if (!UJSON_UTF8_IS_NBYTE(s2)) + return 0; + + s2 &= UJSON_UTF8_NBYTE_MASK; + + (*str)++; + + if (UJSON_UTF8_IS_3BYTE(s0)) + return (s0 & 0x0f)<<12 | s1<<6 | s2; + + (*str)++; + + uint32_t s3 = *str[0]; + + if (!UJSON_UTF8_IS_NBYTE(s2)) + return 0; + + s3 &= UJSON_UTF8_NBYTE_MASK; + + if (UJSON_UTF8_IS_4BYTE(s0)) + return (s0 & 0x07)<<18 | s1<<12 | s2<<6 | s3; + + return 0; +} + +/** + * @brief Returns number of bytes next character is occupying in an UTF-8 string. + * + * @param str A pointer to a string. + * @param off An offset into the string, must point to a valid multibyte boundary. + * @return Number of bytes next character occupies, zero on string end and -1 on failure. + */ +int8_t ujson_utf8_next_chsz(const char *str, size_t off); + +/** + * @brief Returns number of bytes previous character is occupying in an UTF-8 string. + * + * @param str A pointer to a string. + * @param off An offset into the string, must point to a valid multibyte boundary. + * @return Number of bytes previous character occupies, and -1 on failure. + */ +int8_t ujson_utf8_prev_chsz(const char *str, size_t off); + +/** + * @brief Returns a number of characters in UTF-8 string. + * + * Returns number of characters in an UTF-8 string, which may be less or equal + * to what strlen() reports. + * + * @param str An UTF-8 string. + * @return Number of characters in the string. + */ +size_t ujson_utf8_strlen(const char *str); + +/** + * @brief Returns a number of bytes needed to store unicode character into UTF-8. + * + * @param unicode A unicode character. + * @return Number of utf8 bytes required to store a unicode character. + */ +static inline unsigned int ujson_utf8_bytes(uint32_t unicode) +{ + if (unicode < 0x0080) + return 1; + + if (unicode < 0x0800) + return 2; + + if (unicode < 0x10000) + return 3; + + return 4; +} + +/** + * @brief Writes an unicode character into a UTF-8 buffer. + * + * The buffer _must_ be large enough! + * + * @param unicode A unicode character. + * @param buf A byte buffer. + * @return A number of bytes written. + */ +static inline int ujson_to_utf8(uint32_t unicode, char *buf) +{ + if (unicode < 0x0080) { + buf[0] = unicode & 0x007f; + return 1; + } + + if (unicode < 0x0800) { + buf[0] = 0xc0 | (0x1f & (unicode>>6)); + buf[1] = 0x80 | (0x3f & unicode); + return 2; + } + + if (unicode < 0x10000) { + buf[0] = 0xe0 | (0x0f & (unicode>>12)); + buf[1] = 0x80 | (0x3f & (unicode>>6)); + buf[2] = 0x80 | (0x3f & unicode); + return 3; + } + + buf[0] = 0xf0 | (0x07 & (unicode>>18)); + buf[1] = 0x80 | (0x3f & (unicode>>12)); + buf[2] = 0x80 | (0x3f & (unicode>>6)); + buf[3] = 0x80 | (0x3f & unicode); + return 4; +} + +#endif /* UJSON_UTF_H */ diff --git a/include/ujson_writer.h b/include/ujson_writer.h new file mode 100644 index 00000000..dfcc9505 --- /dev/null +++ b/include/ujson_writer.h @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2021-2024 Cyril Hrubis + */ + +/** + * @file ujson_writer.h + * @brief A JSON writer. + * + * All the function that add values return zero on success and non-zero on a + * failure. Once an error has happened all subsequent attempts to add more + * values return with non-zero exit status immediatelly. This is designed + * so that we can add several values without checking each return value + * and only check if error has happened at the end of the sequence. + * + * Failures may occur: + * - if we call the functions out of order, e.g. attempt to finish array when + * we are not writing out an array. + * - if we run out of recursion stack + * - may be propagated from the writer function, e.g. allocation failure, no + * space on disk, etc. + */ + +#ifndef UJSON_WRITER_H +#define UJSON_WRITER_H + +#include + +/** @brief A JSON writer */ +struct ujson_writer { + unsigned int depth; + char depth_type[UJSON_RECURSION_MAX/8]; + char depth_first[UJSON_RECURSION_MAX/8]; + + /** Handler to print errors and warnings */ + void (*err_print)(void *err_print_priv, const char *line); + void *err_print_priv; + char err[UJSON_ERR_MAX]; + + /** Handler to produce JSON output */ + int (*out)(struct ujson_writer *self, const char *buf, size_t buf_size); + void *out_priv; +}; + +/** + * @brief An ujson_writer initializer with default values. + * + * @param vout A pointer to function to write out the data. + * @param vout_priv An user pointer passed to the out function. + * + * @return An ujson_writer initialized with default values. + */ +#define UJSON_WRITER_INIT(vout, vout_priv) { \ + .err_print = UJSON_ERR_PRINT, \ + .err_print_priv = UJSON_ERR_PRINT_PRIV, \ + .out = vout, \ + .out_priv = vout_priv \ +} + +/** + * @brief Allocates a JSON file writer. + * + * The call may fail either when file cannot be opened for writing or if + * allocation has failed. In all cases errno should be set correctly. + * + * @param path A path to the file, file is opened for writing and created if it + * does not exist. + * + * @return A ujson_writer pointer or NULL in a case of failure. + */ +ujson_writer *ujson_writer_file_open(const char *path); + +/** + * @brief Closes and frees a JSON file writer. + * + * @param self A ujson_writer file writer. + * + * @return Zero on success, non-zero on a failure and errno is set. + */ +int ujson_writer_file_close(ujson_writer *self); + +/** + * @brief Returns true if writer error happened. + * + * @param self A JSON writer. + * + * @return True if error has happened. + */ +static inline int ujson_writer_err(ujson_writer *self) +{ + return !!self->err[0]; +} + +/** + * @brief Starts a JSON object. + * + * For a top level object the id must be NULL, every other object has to have + * non-NULL id. The call will also fail if maximal recursion depth + * UJSON_RECURSION_MAX has been reached. + * + * @param self A JSON writer. + * @param id An object name. + * + * @return Zero on a success, non-zero otherwise. + */ +int ujson_obj_start(ujson_writer *self, const char *id); + +/** + * @brief Finishes a JSON object. + * + * The call will fail if we are currenlty not writing out an object. + * + * @param self A JSON writer. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_obj_finish(ujson_writer *self); + +/** + * @brief Starts a JSON array. + * + * For a top level array the id must be NULL, every other array has to have + * non-NULL id. The call will also fail if maximal recursion depth + * UJSON_RECURSION_MAX has been reached. + * + * @param self A JSON writer. + * @param id An array name. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_arr_start(ujson_writer *self, const char *id); + +/** + * @brief Finishes a JSON array. + * + * The call will fail if we are currenlty not writing out an array. + * + * @param self A JSON writer. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_arr_finish(ujson_writer *self); + +/** + * @brief Adds a null value. + * + * The id must be NULL inside of an array, and must be non-NULL inside of an + * object. + * + * @param self A JSON writer. + * @param id A null value name. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_null_add(ujson_writer *self, const char *id); + +/** + * @brief Adds an integer value. + * + * The id must be NULL inside of an array, and must be non-NULL inside of an + * object. + * + * @param self A JSON writer. + * @param id An integer value name. + * @param val An integer value. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_int_add(ujson_writer *self, const char *id, long val); + +/** + * @brief Adds a bool value. + * + * The id must be NULL inside of an array, and must be non-NULL inside of an + * object. + * + * @param self A JSON writer. + * @param id An boolean value name. + * @param val A boolean value. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_bool_add(ujson_writer *self, const char *id, int val); + +/** + * @brief Adds a float value. + * + * The id must be NULL inside of an array, and must be non-NULL inside of an + * object. + * + * @param self A JSON writer. + * @param id A floating point value name. + * @param val A floating point value. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_float_add(ujson_writer *self, const char *id, double val); + +/** + * @brief Adds a string value. + * + * The id must be NULL inside of an array, and must be non-NULL inside of an + * object. + * + * @param self A JSON writer. + * @param id A string value name. + * @param str An UTF8 string value. + * + * @return Zero on success, non-zero otherwise. + */ +int ujson_str_add(ujson_writer *self, const char *id, const char *str); + +/** + * @brief Finalizes json writer. + * + * Finalizes the json writer, throws possible errors through the error printing + * function. + * + * @param self A JSON writer. + * @return Overall error value. + */ +int ujson_writer_finish(ujson_writer *self); + +#endif /* UJSON_WRITER_H */ diff --git a/lib/README.md b/lib/README.md index ccb1cf1d..8340de0d 100755 --- a/lib/README.md +++ b/lib/README.md @@ -27,16 +27,16 @@ When a test is executed the very first thing to happen is that we check for various test prerequisites. These are described in the tst\_test structure and -range from simple '.require\_root' to a more complicated kernel .config boolean +range from simple '.needs\_root' to a more complicated kernel .config boolean expressions such as: "CONFIG\_X86\_INTEL\_UMIP=y | CONFIG\_X86\_UMIP=y". -If all checks are passed the process carries on with setting up the test +If all checks are passed, the process continues with setting up the test environment as requested in the tst\_test structure. There are many different setup steps that have been put into the test library again ranging from rather simple creation of a unique test temporary directory to a bit more complicated ones such as preparing, formatting, and mounting a block device. -The test library also intializes shrared memory used for IPC at this step. +The test library also initializes shared memory used for IPC at this step. Once all the prerequisites are checked and test environment has been prepared we can move on executing the testcase itself. The actual test is executed in a @@ -62,9 +62,9 @@ for test_variants: fork_testrun() ``` -Before we fork() the test process the test library sets up a timeout alarm and -also a heartbeat signal handlers and also sets up an alarm(2) accordingly to -the test timeout. When a test times out the test library gets SIGALRM and the +Before we fork the test process, the test library sets up a timeout alarm and +a heartbeat signal handler and it also sets up an alarm(2) accordingly to +the test timeout. When a test times out, the test library gets SIGALRM and the alarm handler mercilessly kills all forked children by sending SIGKILL to the whole process group. The heartbeat handler is used by the test process to reset this timer for example when the test functions run in a loop. @@ -97,14 +97,14 @@ usually better option than exiting it in the middle. The test cleanup() is also called by the tst\_brk() handler in order to cleanup before exiting the test process, hence it must be able to cope even with partial test setup. Usually it suffices to make sure to clean up only -resources that already have been set up and to do that in an inverse order that -we did in setup(). +resources that already have been set up and to do that in the reverse order +that we did in setup(). Once the test process exits or leaves the run() or run\_all() function the test library wakes up from the waitpid() call, and checks if the test process exited normally. -Once the testrun is finished the test library does a cleanup() as well to clean +Once the testrun is finished, the test library does a cleanup() as well to clean up resources set up in the test library setup(), reports test results and finally exits the process. @@ -126,8 +126,8 @@ This especially means that: - While the test results are, by the design, propagated to the test library we may still miss a child that gets killed by a signal or exits unexpectedly. -The test writer should, because of this, take care for reaping these proceses -properly, in most cases this could be simply done by calling +The test writer should, because of this, take care of reaping these +processes properly, in most cases this could be simply done by calling tst\_reap\_children() to collect and dissect deceased. Also note that tst\_brk() does exit only the current process, so if a child @@ -136,9 +136,9 @@ exits. ### Test library and exec() -The piece of mapped memory to store the results to is not preserved over +The piece of mapped memory to store the results is not preserved over exec(2), hence to use the test library from a binary started by an exec() it -has to be remaped. In this case the process must to call tst\_reinit() before +has to be remapped. In this case, the process must call tst\_reinit() before calling any other library functions. In order to make this happen the program environment carries LTP\_IPC\_PATH variable with a path to the backing file on tmpfs. This also allows us to use the test library from shell testcases. @@ -148,5 +148,5 @@ tmpfs. This also allows us to use the test library from shell testcases. The piece of mapped memory is also used as a base for a futex-based synchronization primitives called checkpoints. And as said previously the memory can be mapped to any process by calling the tst\_reinit() function. As a -matter of a fact there is even a tst\_checkpoint binary that allows us to use +matter of a fact, there is even a tst\_checkpoint binary that allows us to use the checkpoints from shell code as well. diff --git a/lib/cloner.c b/lib/cloner.c index 95954f6d..00cbb898 100755 --- a/lib/cloner.c +++ b/lib/cloner.c @@ -124,42 +124,3 @@ void *ltp_alloc_stack(size_t size) 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/lib/newlib_tests/.gitignore b/lib/newlib_tests/.gitignore index 0256bef7..a4984d2e 100755 --- a/lib/newlib_tests/.gitignore +++ b/lib/newlib_tests/.gitignore @@ -15,6 +15,11 @@ tst_capability01 tst_capability02 tst_cgroup01 tst_cgroup02 +tst_checkpoint +tst_checkpoint_parent +tst_checkpoint_child +tst_checkpoint_wait_timeout +tst_checkpoint_wake_timeout tst_device tst_safe_fileops tst_res_hexd @@ -31,6 +36,7 @@ test_exec_child test_kconfig test_kconfig01 test_kconfig02 +test_kconfig03 variant test_guarded_buf tst_bool_expr @@ -56,3 +62,11 @@ tst_needs_cmds08 test_runtime01 test_runtime02 test_children_cleanup +tst_res_flags +tst_safe_sscanf +test_brk_child +test_brk_fail +test_brk_parent +test_brk_pass +test_brk_variant +test_fail_variant diff --git a/lib/newlib_tests/runtest.sh b/lib/newlib_tests/runtest.sh index be707fe6..d87751c2 100755 --- a/lib/newlib_tests/runtest.sh +++ b/lib/newlib_tests/runtest.sh @@ -1,14 +1,50 @@ #!/bin/sh -# Copyright (c) 2021 Petr Vorel +# Copyright (c) 2021-2024 Petr Vorel -LTP_C_API_TESTS="${LTP_C_API_TESTS:-test05 test07 test09 test15 test_runtime01 -tst_needs_cmds01 tst_needs_cmds02 tst_needs_cmds03 tst_needs_cmds06 -tst_needs_cmds07 tst_bool_expr test_exec test_timer tst_res_hexd tst_strstatus -tst_fuzzy_sync03 test_zero_hugepage.sh test_kconfig.sh -test_children_cleanup.sh}" +# TODO "unknown failure, exit code": test_assert test08 tst_cgroup01 tst_cgroup02 tst_res_flags variant +# TODO TFAIL: test_macros0[1-6] test23 test26 +# TODO TBROK: test_exec_child test_kconfig01 test_kconfig02 tst_needs_cmds04 tst_needs_cmds05 test_runtime02 test01 test02 test03 test04 test06 test11 test13 test22 test25 tst_safe_fileops +# TODO TWARN: test_guarded_buf test14 tst_capability01 tst_print_result +LTP_C_API_TESTS="${LTP_C_API_TESTS:- +test_children_cleanup.sh +test_kconfig.sh +test_kconfig03 +test_parse_filesize +test_runtime01 +test_timer +test_zero_hugepage.sh +test0[579] +test1[59] +test2[04] +tst_bool_expr +tst_capability02 +tst_checkpoint +tst_checkpoint_parent +tst_checkpoint_wait_timeout +tst_checkpoint_wake_timeout +tst_device +tst_expiration_timer +tst_fuzzy_sync0[1-3] +tst_needs_cmds0[1-36-8] +tst_res_hexd +tst_safe_sscanf +tst_strstatus}" -LTP_SHELL_API_TESTS="${LTP_SHELL_API_TESTS:-shell/tst_check_driver.sh -shell/tst_check_kconfig0[1-5].sh shell/tst_errexit.sh shell/net/*.sh}" +# TODO "unknown failure, exit code": shell/tst_res_flags.sh shell/timeout03.sh +# TODO TBROK: shell/test_timeout.sh (sometimes) shell/timeout04.sh +LTP_SHELL_API_TESTS="${LTP_SHELL_API_TESTS:- +shell/timeout0[1-2].sh +shell/tst_all_filesystems.sh +shell/tst_all_filesystems_skip.sh +shell/tst_device_size.sh +shell/tst_errexit.sh +shell/tst_format_device.sh +shell/tst_check_driver.sh +shell/tst_check_kconfig0[1-5].sh +shell/tst_mount_device.sh +shell/tst_mount_device_tmpfs.sh +shell/tst_skip_filesystems.sh +shell/net/*.sh}" cd $(dirname $0) PATH="$PWD/../../testcases/lib/:$PATH" diff --git a/lib/newlib_tests/shell/tst_device_size.sh b/lib/newlib_tests/shell/tst_device_size.sh new file mode 100644 index 00000000..75143797 --- /dev/null +++ b/lib/newlib_tests/shell/tst_device_size.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024 Petr Vorel + +TST_NEEDS_DEVICE=1 +TST_DEVICE_SIZE=10 +TST_TESTFUNC=test + +test() +{ + tst_res TPASS "TST_DEVICE_SIZE=$TST_DEVICE_SIZE" + # overlayfs adds 1 MB + EXPECT_PASS "du -ms . | grep -qw -e $TST_DEVICE_SIZE -e $(($TST_DEVICE_SIZE + 1))" +} + +. tst_test.sh +tst_run diff --git a/lib/newlib_tests/shell/tst_res_flags.sh b/lib/newlib_tests/shell/tst_res_flags.sh new file mode 100644 index 00000000..bca3d26c --- /dev/null +++ b/lib/newlib_tests/shell/tst_res_flags.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2023 Petr Vorel + +TST_TESTFUNC=test +TST_CLEANUP=cleanup + +test() +{ + tst_res TPASS "TPASS message" + tst_res TFAIL "TFAIL message" + tst_res TBROK "TBROK message" + tst_res TCONF "TCONF message" + tst_res TWARN "TWARN message" + tst_res TINFO "TINFO message" +} + +cleanup() +{ + tst_brk TBROK "TBROK message should be TWARN in cleanup" +} + +. tst_test.sh +tst_run diff --git a/lib/newlib_tests/test08.c b/lib/newlib_tests/test08.c index 5099b08d..d48bf29e 100755 --- a/lib/newlib_tests/test08.c +++ b/lib/newlib_tests/test08.c @@ -22,7 +22,7 @@ static void setup(void) static void cleanup(void) { - static int flag; + static tst_atomic_t flag; /* Avoid subsequent threads to enter the cleanup */ if (tst_atomic_inc(&flag) != 1) diff --git a/lib/newlib_tests/test09.c b/lib/newlib_tests/test09.c index 0f42bacc..eae258e2 100755 --- a/lib/newlib_tests/test09.c +++ b/lib/newlib_tests/test09.c @@ -13,7 +13,7 @@ #define THREADS 64 #define ITERATIONS 100000 -static int atomic; +static tst_atomic_t atomic; static void *worker(void *id) { diff --git a/lib/newlib_tests/test15.c b/lib/newlib_tests/test15.c index 3a2ac362..c63da18f 100755 --- a/lib/newlib_tests/test15.c +++ b/lib/newlib_tests/test15.c @@ -39,7 +39,7 @@ struct block { intptr_t filler[FILLER]; }; -static int atomic; +static tst_atomic_t atomic; /* Instead of storing seq_n on the stack (probably next to the atomic variable * above), we store it in the middle of some anonymous mapped memory and keep * a pointer to it. This should decrease the probability that the value of diff --git a/lib/newlib_tests/test_brk_child.c b/lib/newlib_tests/test_brk_child.c new file mode 100644 index 00000000..68c50ed1 --- /dev/null +++ b/lib/newlib_tests/test_brk_child.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Cyril Hrubis + */ + +/* + * Test that tst_brk(TFAIL, ...) exits only single test variant. + */ +#include "tst_test.h" + +static void do_test(void) +{ + int i; + + for (i = 0; i < 10; i++) { + if (!SAFE_FORK()) { + tst_res(TINFO, "Suspending child %i", i); + pause(); + } + } + + if (!SAFE_FORK()) + tst_brk(TBROK, "Child triggers TBROK"); + + pause(); +} + +static struct tst_test test = { + .test_all = do_test, + .forks_child = 1, +}; diff --git a/lib/newlib_tests/test_brk_fail.c b/lib/newlib_tests/test_brk_fail.c new file mode 100644 index 00000000..ee1e554a --- /dev/null +++ b/lib/newlib_tests/test_brk_fail.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + */ + +/* + * Test that tst_brk(TFAIL, ...) works properly. + */ +#include "tst_test.h" + +static void do_test(void) +{ + int pid = SAFE_FORK(); + + if (pid) + return; + + tst_brk(TFAIL, "Test child exiting..."); + tst_res(TWARN, "Test child stil alive!"); +} + +static struct tst_test test = { + .test_all = do_test, + .forks_child = 1, +}; diff --git a/lib/newlib_tests/test_brk_parent.c b/lib/newlib_tests/test_brk_parent.c new file mode 100644 index 00000000..974e7782 --- /dev/null +++ b/lib/newlib_tests/test_brk_parent.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Cyril Hrubis + */ + +/* + * Test that tst_brk(TFAIL, ...) exits only single test variant. + */ +#include "tst_test.h" + +static void do_test(void) +{ + int i; + + for (i = 0; i < 10; i++) { + if (!SAFE_FORK()) { + tst_res(TINFO, "Suspending child %i", i); + pause(); + } + } + + tst_brk(TBROK, "Parent triggers TBROK"); +} + +static struct tst_test test = { + .test_all = do_test, + .forks_child = 1, +}; diff --git a/lib/newlib_tests/test_brk_pass.c b/lib/newlib_tests/test_brk_pass.c new file mode 100644 index 00000000..13527078 --- /dev/null +++ b/lib/newlib_tests/test_brk_pass.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + */ + +/* + * Test that tst_brk(TPASS, ...) works properly. + */ +#include "tst_test.h" + +static void do_test(void) +{ + int pid = SAFE_FORK(); + + if (pid) + return; + + tst_brk(TPASS, "Test child exiting..."); + tst_res(TWARN, "Test child stil alive!"); +} + +static struct tst_test test = { + .test_all = do_test, + .forks_child = 1, +}; diff --git a/lib/newlib_tests/test_brk_variant.c b/lib/newlib_tests/test_brk_variant.c new file mode 100644 index 00000000..6f91a72a --- /dev/null +++ b/lib/newlib_tests/test_brk_variant.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + */ + +/* + * Test that tst_brk(TBROK, ...) exits the test immediately. + */ +#include "tst_test.h" + +static void do_test(void) +{ + tst_brk(TBROK, "Exitting the test during the first variant"); +} + +static struct tst_test test = { + .test_all = do_test, + .test_variants = 10, +}; diff --git a/lib/newlib_tests/test_fail_variant.c b/lib/newlib_tests/test_fail_variant.c new file mode 100644 index 00000000..27829c70 --- /dev/null +++ b/lib/newlib_tests/test_fail_variant.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + */ + +/* + * Test that tst_brk(TFAIL, ...) exits only single test variant. + */ +#include "tst_test.h" + +static void do_test(void) +{ + tst_brk(TFAIL, "Failing a test variant"); + tst_res(TWARN, "Shouldn't be reached"); +} + +static struct tst_test test = { + .test_all = do_test, + .test_variants = 10, +}; diff --git a/lib/newlib_tests/test_kconfig03.c b/lib/newlib_tests/test_kconfig03.c new file mode 100644 index 00000000..52c0c1b4 --- /dev/null +++ b/lib/newlib_tests/test_kconfig03.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Li Wang + */ + +#include "tst_test.h" +#include "tst_kconfig.h" + +static struct tst_kcmdline_var params[] = { + TST_KCMDLINE_INIT("BOOT_IMAGE"), + TST_KCMDLINE_INIT("root"), + TST_KCMDLINE_INIT("foo") +}; + +static void do_test(void) +{ + int i, N; + + N = (int) ARRAY_SIZE(params); + + tst_kcmdline_parse(params, N); + + for (i = 0; i < N; i++) { + if (params[i].found) { + tst_res(TPASS, "params[%d] = {%s, %s}", i, params[i].key, params[i].value); + } else { + if (!strcmp(params[i].key, "foo")) + tst_res(TPASS, "%s is not found in /proc/cmdline", params[i].key); + else + tst_res(TFAIL, "%s is not found in /proc/cmdline", params[i].key); + } + } +} + +static struct tst_test test = { + .test_all = do_test, +}; diff --git a/lib/newlib_tests/test_macros02.c b/lib/newlib_tests/test_macros02.c index 647f7368..1b12fab7 100755 --- a/lib/newlib_tests/test_macros02.c +++ b/lib/newlib_tests/test_macros02.c @@ -1,10 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Cyril Hrubis + * Copyright (c) Linux Test Project, 2021-2024 */ /* - * Test TST_EXP_FAIL and TST_EXP_FAIL2 macro. + * Test macros: + * + * - TST_EXP_FAIL + * - TST_EXP_FAIL_ARR + * - TST_EXP_FAIL2 + * - TST_EXP_FAIL2_ARR + * - TST_EXP_FAIL_PTR_NULL + * - TST_EXP_FAIL_PTR_NULL_ARR + * - TST_EXP_FAIL_PTR_VOID + * - TST_EXP_FAIL_PTR_VOID_ARR */ #include "tst_test.h" @@ -25,27 +35,56 @@ static int inval_ret_fn(void) return 42; } +static char *fail_fn_null(void) +{ + errno = EINVAL; + return NULL; +} + +static char *fail_fn_void(void) +{ + errno = EINVAL; + return (void *)-1; +} + +static char *inval_ret_fn_char(void) +{ + return (void *)-1; +} + +static char *pass_fn_char(void) +{ + return "pass"; +} + +#define TEST_MACRO(macro, macro_arr, fail_fn, pass_fn, inval_fn) \ + do { \ + tst_res(TINFO, "Testing " #macro " macro"); \ + macro(fail_fn(), EINVAL, #fail_fn"()"); \ + tst_res(TINFO, "TST_PASS = %i", TST_PASS); \ + macro(fail_fn(), ENOTTY); /* skip msg parameter */ \ + tst_res(TINFO, "TST_PASS = %i", TST_PASS); \ + macro(pass_fn(), ENOTTY, #pass_fn"()"); \ + tst_res(TINFO, "TST_PASS = %i", TST_PASS); \ + macro(inval_fn(), ENOTTY, #inval_fn"()"); \ + tst_res(TINFO, "TST_PASS = %i", TST_PASS); \ + macro_arr(fail_fn(), exp_errs_pass, ARRAY_SIZE(exp_errs_pass), #fail_fn"()"); \ + tst_res(TINFO, "TST_PASS = %i", TST_PASS); \ + macro_arr(fail_fn(), exp_errs_fail, ARRAY_SIZE(exp_errs_fail)); /* skip msg parameter */ \ + tst_res(TINFO, "TST_PASS = %i", TST_PASS); \ + } while (0) + static void do_test(void) { - tst_res(TINFO, "Testing TST_EXP_FAIL macro"); - TST_EXP_FAIL(fail_fn(), EINVAL, "fail_fn()"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); - TST_EXP_FAIL(fail_fn(), ENOTTY, "fail_fn()"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); - TST_EXP_FAIL(pass_fn(), ENOTTY, "pass_fn()"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); - TST_EXP_FAIL(inval_ret_fn(), ENOTTY, "inval_ret_fn()"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); + const int exp_errs_pass[] = {ENOTTY, EINVAL}; + const int exp_errs_fail[] = {ENOTTY, EISDIR}; - tst_res(TINFO, "Testing TST_EXP_FAIL2 macro"); - TST_EXP_FAIL2(fail_fn(), EINVAL, "fail_fn()"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); - TST_EXP_FAIL2(fail_fn(), ENOTTY, "fail_fn()"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); - TST_EXP_FAIL2(pass_fn(), ENOTTY, "pass_fn"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); - TST_EXP_FAIL2(inval_ret_fn(), ENOTTY, "inval_ret_fn()"); - tst_res(TINFO, "TST_PASS = %i", TST_PASS); + TEST_MACRO(TST_EXP_FAIL, TST_EXP_FAIL_ARR, fail_fn, pass_fn, inval_ret_fn); + TEST_MACRO(TST_EXP_FAIL2, TST_EXP_FAIL2_ARR, fail_fn, pass_fn, inval_ret_fn); + TEST_MACRO(TST_EXP_FAIL_PTR_NULL, TST_EXP_FAIL_PTR_NULL_ARR, fail_fn_null, + pass_fn_char, inval_ret_fn_char); + TEST_MACRO(TST_EXP_FAIL_PTR_VOID, TST_EXP_FAIL_PTR_VOID_ARR, fail_fn_void, + pass_fn_char, inval_ret_fn_char); } static struct tst_test test = { diff --git a/lib/newlib_tests/test_runtime01.c b/lib/newlib_tests/test_runtime01.c index 5e027546..16903385 100644 --- a/lib/newlib_tests/test_runtime01.c +++ b/lib/newlib_tests/test_runtime01.c @@ -25,6 +25,6 @@ static void run(void) static struct tst_test test = { .test_all = run, - .max_runtime = 4, + .runtime = 4, .test_variants = 2, }; diff --git a/lib/newlib_tests/test_runtime02.c b/lib/newlib_tests/test_runtime02.c index 6d89cb53..dd8eaa4d 100644 --- a/lib/newlib_tests/test_runtime02.c +++ b/lib/newlib_tests/test_runtime02.c @@ -24,5 +24,5 @@ static void run(void) static struct tst_test test = { .test_all = run, - .max_runtime = 5, + .runtime = 5, }; diff --git a/lib/newlib_tests/tst_checkpoint.c b/lib/newlib_tests/tst_checkpoint.c new file mode 100644 index 00000000..a283f03e --- /dev/null +++ b/lib/newlib_tests/tst_checkpoint.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Modernized checkpoint usage. + */ + +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_checkpoint.h" + +/* Test 1: Basic checkpoint signal from child to parent */ +static void checkpoint_test1(void) +{ + pid_t pid = SAFE_FORK(); + + if (pid == 0) { + tst_res(TINFO, "Child: signaling checkpoint"); + TST_CHECKPOINT_WAKE(0); + _exit(0); + } + + TST_CHECKPOINT_WAIT(0); + tst_res(TPASS, "Parent: checkpoint reached"); + + SAFE_WAITPID(pid, NULL, 0); + + return; +} + +/* Test 2: Checkpoint wait with timeout, wake from child */ +static void checkpoint_test2(void) +{ + pid_t pid = SAFE_FORK(); + + if (pid == 0) { + tst_res(TINFO, "Child: signaling checkpoint"); + TST_CHECKPOINT_WAKE2(0, 1); + _exit(0); + } + + TST_CHECKPOINT_WAIT2(0, 1000); + tst_res(TPASS, "Parent: checkpoint reached"); + + SAFE_WAITPID(pid, NULL, 0); + + return; +} + +/* Test 3: Wake two child waiters on the same checkpoint */ +static void checkpoint_test3(void) +{ + pid_t pid1, pid2; + + pid1 = SAFE_FORK(); + if (pid1 == 0) { + tst_res(TINFO, "Child 1: waiting on checkpoint 0 (no timeout)"); + TST_CHECKPOINT_WAIT(0); + _exit(0); + } + + pid2 = SAFE_FORK(); + if (pid2 == 0) { + tst_res(TINFO, "Child 2: waiting on checkpoint 0 (1000ms timeout)"); + TST_CHECKPOINT_WAIT2(0, 1000); + _exit(0); + } + + TST_CHECKPOINT_WAKE2(0, 2); + tst_res(TPASS, "Parent: checkpoint wake issued"); + + tst_reap_children(); + + return; +} + +/* Test 4: Two-way checkpoint handshake (child->parent->child) */ +static void checkpoint_test4(void) +{ + pid_t pid = SAFE_FORK(); + + if (pid == 0) { + tst_res(TINFO, "Child: waking and then waiting on checkpoint 0"); + TST_CHECKPOINT_WAKE_AND_WAIT(0); + _exit(0); + } + + TST_CHECKPOINT_WAIT(0); + TST_CHECKPOINT_WAKE(0); + + tst_res(TPASS, "Parent: checkpoint handshake completed"); + + SAFE_WAITPID(pid, NULL, 0); + + return; +} + +static void run(void) +{ + checkpoint_test1(); + checkpoint_test2(); + checkpoint_test3(); + checkpoint_test4(); + + return; +} + +static struct tst_test test = { + .test_all = run, + .forks_child = 1, + .needs_checkpoints = 1, +}; diff --git a/lib/newlib_tests/tst_checkpoint_child.c b/lib/newlib_tests/tst_checkpoint_child.c new file mode 100644 index 00000000..c4eaccac --- /dev/null +++ b/lib/newlib_tests/tst_checkpoint_child.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Child process: this binary is expected to be exec'd by the parent. + * + * It reinitializes the shared memory region using tst_reinit(), + * verifies the command-line argument, and signals checkpoint 0. + */ + +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" +#include "tst_checkpoint.h" + +int main(int argc, char *argv[]) +{ + tst_reinit(); + + if (argc != 2) + tst_brk(TFAIL, "argc is %d, expected 2", argc); + + if (strcmp(argv[1], "canary")) + tst_brk(TFAIL, "argv[1] is %s, expected 'canary'", argv[1]); + + tst_res(TINFO, "Child: signaling checkpoint"); + TST_CHECKPOINT_WAKE(0); + + return 0; +} diff --git a/lib/newlib_tests/tst_checkpoint_parent.c b/lib/newlib_tests/tst_checkpoint_parent.c new file mode 100644 index 00000000..9d7f1783 --- /dev/null +++ b/lib/newlib_tests/tst_checkpoint_parent.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Parent process: spawns a child which reinitializes checkpoint region + * using tst_reinit(). Waits for a checkpoint signal from the child. + */ + +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_checkpoint.h" + +static void run(void) +{ + pid_t pid = SAFE_FORK(); + + if (pid == 0) { + TEST(execlp("tst_checkpoint_child", "tst_checkpoint_child", "canary", NULL)); + tst_brk(TFAIL | TTERRNO, "Failed to execute tst_checkpoint_child"); + } + + TST_CHECKPOINT_WAIT(0); + tst_res(TPASS, "Parent: checkpoint reached"); + + SAFE_WAITPID(pid, NULL, 0); + + return; +} + +static struct tst_test test = { + .forks_child = 1, + .needs_checkpoints = 1, + .child_needs_reinit = 1, + .test_all = run, +}; diff --git a/lib/newlib_tests/tst_checkpoint_wait_timeout.c b/lib/newlib_tests/tst_checkpoint_wait_timeout.c new file mode 100644 index 00000000..2fa6f8cf --- /dev/null +++ b/lib/newlib_tests/tst_checkpoint_wait_timeout.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Test: checkpoint wait with timeout. + * Expected: child blocks on checkpoint wait, parent exits without signaling. + */ + +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_checkpoint.h" + +static void run(void) +{ + pid_t pid; + + pid = SAFE_FORK(); + + if (pid == 0) { + int ret = tst_checkpoint_wait(0, 1000); + + if (ret == -1 && errno == ETIMEDOUT) + tst_res(TPASS, "Child: checkpoint wait timed out as expected"); + else + tst_brk(TBROK | TERRNO, "checkpoint wait failed"); + + _exit(0); + } + + tst_res(TINFO, "Parent: exiting without signaling checkpoint"); + SAFE_WAITPID(pid, NULL, 0); + + return; +} + +static struct tst_test test = { + .test_all = run, + .forks_child = 1, + .needs_checkpoints = 1, +}; diff --git a/lib/newlib_tests/tst_checkpoint_wake_timeout.c b/lib/newlib_tests/tst_checkpoint_wake_timeout.c new file mode 100644 index 00000000..c37fd694 --- /dev/null +++ b/lib/newlib_tests/tst_checkpoint_wake_timeout.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Test: checkpoint wake without matching wait. + * Expected: wake completes with ETIMEDOUT errno as expected. + */ + +#include +#include +#include + +#include "tst_test.h" +#include "tst_checkpoint.h" + +static void run(void) +{ + int ret = tst_checkpoint_wake(0, 1, 1000); + + if (ret == -1 && errno == ETIMEDOUT) + tst_res(TPASS, "checkpoint wake timed out as expected"); + else + tst_brk(TBROK | TERRNO, "checkpoint wake failed"); + + return; +} + +static struct tst_test test = { + .test_all = run, + .needs_checkpoints = 1, +}; diff --git a/lib/newlib_tests/tst_device.c b/lib/newlib_tests/tst_device.c index 53099f9b..68920992 100755 --- a/lib/newlib_tests/tst_device.c +++ b/lib/newlib_tests/tst_device.c @@ -28,11 +28,8 @@ static int set_block_size(int fd) static void setup(void) { int fd; - int ret; - ret = asprintf(&mntpoint, "%s/mnt", tst_get_tmpdir()); - if (ret < 0) - tst_brk(TBROK, "asprintf failure"); + mntpoint = tst_tmpdir_genpath("mnt"); fd = SAFE_OPEN(tst_device->dev, O_RDONLY); @@ -50,7 +47,7 @@ static void setup(void) static void cleanup(void) { - if (tst_is_mounted(mntpoint)) + if (!access(mntpoint, F_OK) && tst_is_mounted(mntpoint)) SAFE_UMOUNT(mntpoint); } diff --git a/lib/newlib_tests/tst_fuzzy_sync01.c b/lib/newlib_tests/tst_fuzzy_sync01.c index d0748958..b1390f46 100755 --- a/lib/newlib_tests/tst_fuzzy_sync01.c +++ b/lib/newlib_tests/tst_fuzzy_sync01.c @@ -2,15 +2,13 @@ /* * Copyright (c) 2021 Richard Palethorpe */ -/*\ - * [Description] - * +/* * This verifies Fuzzy Sync's basic ability to reproduce a particular * outcome to a data race when the critical sections are not aligned. * * We make the simplifying assumptions that: * - Each thread contains a single contiguous critical section. - * - The threads only interact through a single variable. + * - The threads only interact through a single variable(H: Hit). * - The various timings are constant except for variations introduced * by the environment. * @@ -50,9 +48,9 @@ * range is required. * * When entering their critical sections, both threads increment the - * 'c' counter variable atomically. They both also increment it when - * leaving their critical sections. We record the value of 'c' when A - * increments it. From the recorded values of 'c' we can deduce if the + * 'H' counter variable atomically. They both also increment it when + * leaving their critical sections. We record the value of 'H' when A + * increments it. From the recorded values of 'H' we can deduce if the * critical sections overlap and their ordering. * * Start (cs) | End (ct) | Ordering @@ -62,7 +60,7 @@ * * Any other combination of 'cs' and 'ct' means the critical sections * overlapped. -\*/ + */ #include "tst_test.h" #include "tst_fuzzy_sync.h" @@ -90,7 +88,7 @@ struct race { const struct window b; }; -static int c; +static tst_atomic_t H; static struct tst_fzsync_pair pair; static const struct race races[] = { @@ -162,15 +160,15 @@ static void *worker(void *v) const struct window b = races[i].b; while (tst_fzsync_run_b(&pair)) { - if (tst_atomic_load(&c)) + if (tst_atomic_load(&H)) tst_brk(TBROK, "Counter should now be zero"); tst_fzsync_start_race_b(&pair); delay(b.critical_s); - tst_atomic_add_return(1, &c); + tst_atomic_add_return(1, &H); delay(b.critical_t); - tst_atomic_add_return(1, &c); + tst_atomic_add_return(1, &H); delay(b.return_t); tst_fzsync_end_race_b(&pair); @@ -192,9 +190,9 @@ static void run(unsigned int i) tst_fzsync_start_race_a(&pair); delay(a.critical_s); - cs = tst_atomic_add_return(1, &c); + cs = tst_atomic_add_return(1, &H); delay(a.critical_t); - ct = tst_atomic_add_return(1, &c); + ct = tst_atomic_add_return(1, &H); delay(a.return_t); tst_fzsync_end_race_a(&pair); @@ -206,16 +204,37 @@ static void run(unsigned int i) else critical++; - r = tst_atomic_add_return(-4, &c); + r = tst_atomic_add_return(-4, &H); if (r) tst_brk(TBROK, "cs = %d, ct = %d, r = %d", cs, ct, r); if (critical > 100) { tst_fzsync_pair_cleanup(&pair); + tst_atomic_store(0, &pair.exit); break; } } + /* + * If `pair->exit` is true, the test may fail to meet expected + * results due to resource constraints in shared CI environments + * (e.g., GitHub Actions). Limited control over CPU allocation + * can cause delays or interruptions in CPU time slices due to + * contention with other jobs. + * + * Binding the test to a single CPU core (e.g., via `taskset -c 0`) + * can worsen this by increasing contention, leading to performance + * degradation and premature loop termination. + * + * To ensure valid and reliable results in scenarios (e.g., HW, VM, CI), + * it is best to ignore test result when loop termination occurs, + * avoiding unnecessary false positive. + */ + if (pair.exit) { + tst_res(TCONF, "Test may not be able to generate a valid result"); + return; + } + tst_res(critical > 50 ? TPASS : TFAIL, "acs:%-2d act:%-2d art:%-2d | =:%-4d -:%-4d +:%-4d", a.critical_s, a.critical_t, a.return_t, @@ -227,5 +246,5 @@ static struct tst_test test = { .test = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 150, + .runtime = 150, }; diff --git a/lib/newlib_tests/tst_fuzzy_sync02.c b/lib/newlib_tests/tst_fuzzy_sync02.c index afe4973b..bc079f6f 100755 --- a/lib/newlib_tests/tst_fuzzy_sync02.c +++ b/lib/newlib_tests/tst_fuzzy_sync02.c @@ -2,16 +2,14 @@ /* * Copyright (c) 2021 Richard Palethorpe */ -/*\ - * [Description] - * +/* * This verifies Fuzzy Sync's ability to reproduce a particular * outcome to a data race when multiple races are present. * * We make the simplifying assumptions that: * - There is one data race we want to hit and one to avoid. * - Each thread contains two contiguous critical sections. One for each race. - * - The threads only interact through two variables, one for each race. + * - The threads only interact through two variables(H: Hit, D: Avoid), one for each race. * - If we hit the race we want to avoid then it causes thread A to exit early. * * We don't consider more complicated dynamic interactions between the @@ -22,7 +20,7 @@ * threshold of complexity, increasing the complexity of the race is * no different from adding random noise. * - * Emperically this appears to be true. So far we have seen in + * Empirically this appears to be true. So far we have seen in * reproducers that there are no more than two significant data * races. One we wish to reproduce and one we wish to avoid. It is * possible that the code contains multiple data races, but that they @@ -35,7 +33,7 @@ * Here we only test a bias to delay B. A delay of A would be * identical except that the necessary delay bias would be negative. * -\*/ + */ #include "tst_test.h" #include "tst_fuzzy_sync.h" @@ -63,14 +61,35 @@ struct race { const struct window b; }; -static int c, d; +static tst_atomic_t H, D; static struct tst_fzsync_pair pair; +/** + * Race 1: + * Thread A: |---(1)--|[1]|---(1)---| + * Thread B: |---(1)--|[1]|---(1)---| + * ad (A): |---(0)|[1]|(0)---| + * bd (B): |---(0)|[1]|(0)---| + * + * Race 2: + * Thread A: |------------------(30)------------------|[1]|---(1)---| + * Thread B: |---(1)--|[1]|---(1)---| + * ad (A): |---(0)|[1]|--(0)---| + * bd (B): |---(0)|[20]|--(0)---| + * + * Race 3: + * Thread A: |--------------------(40)--------------------|[1]|---(0)---| + * Thread B: |---(1)--|[1]|------------------(20)------------------| + * ad (A): |---(1)--|[10]|--(0)---| + * bd (B): |---(1)--|[10]|--(0)---| + */ static const struct race races[] = { { .a = { 1, 1, 1 }, .b = { 1, 1, 1 }, .ad = { 0, 1, 0 }, .bd = { 0, 1, 0 } }, + { .a = { 30, 1, 1 }, .b = { 1, 1, 1 }, .ad = { 0, 1, 0 }, .bd = { 0, 20, 0 } }, + { .a = { 40, 1, 0 }, .b = { 1, 1, 20 }, .ad = { 1, 10, 0 }, .bd = { 1, 10, 0 } }, }; @@ -87,6 +106,20 @@ static void setup(void) tst_fzsync_pair_init(&pair); } +/** + * to_abs() - Convert relative time intervals to absolute time points + * @w: The input window structure containing relative time intervals + * + * This function converts relative time intervals (represented in the + * struct window) into absolute time points, where: + * + * - The start of the critical section is `critical_s`. + * - The end of the critical section is calculated as `critical_s + critical_t`. + * - The end of execution is calculated as `critical_s + critical_t + return_t`. + * + * Return: + * A new window structure (`wc`) with absolute time points. + */ static struct window to_abs(const struct window w) { const struct window wc = { @@ -109,9 +142,9 @@ static void *worker(void *v) tst_fzsync_start_race_b(&pair); for (now = 0; now <= fin; now++) { if (now == b.critical_s || now == b.critical_t) - tst_atomic_add_return(1, &c); + tst_atomic_add_return(1, &H); if (now == bd.critical_s || now == bd.critical_t) - tst_atomic_add_return(1, &d); + tst_atomic_add_return(1, &D); sched_yield(); } @@ -132,19 +165,19 @@ static void run(unsigned int i) SAFE_PTHREAD_CREATE(&pair.thread_b, 0, worker, &i); while (tst_fzsync_run_a(&pair)) { - c = 0; - d = 0; + H = 0; + D = 0; fin = a.return_t; tst_fzsync_start_race_a(&pair); for (now = 0; now <= fin; now++) { if (now >= ad.critical_s && - now <= ad.critical_t && tst_atomic_load(&d) > 0) + now <= ad.critical_t && tst_atomic_load(&D) > 0) fin = ad.return_t; if (now >= a.critical_s && - now <= a.critical_t && tst_atomic_load(&c) == 1) { - tst_atomic_add_return(1, &c); + now <= a.critical_t && tst_atomic_load(&H) == 1) { + tst_atomic_add_return(1, &H); critical++; } @@ -157,10 +190,31 @@ static void run(unsigned int i) if (critical > 100) { tst_fzsync_pair_cleanup(&pair); + tst_atomic_store(0, &pair.exit); break; } } + /* + * If `pair->exit` is true, the test may fail to meet expected + * results due to resource constraints in shared CI environments + * (e.g., GitHub Actions). Limited control over CPU allocation + * can cause delays or interruptions in CPU time slices due to + * contention with other jobs. + * + * Binding the test to a single CPU core (e.g., via `taskset -c 0`) + * can worsen this by increasing contention, leading to performance + * degradation and premature loop termination. + * + * To ensure valid and reliable results in scenarios (e.g., HW, VM, CI), + * it is best to ignore test result when loop termination occurs, + * avoiding unnecessary false positive. + */ + if (pair.exit) { + tst_res(TCONF, "Test may not be able to generate a valid result"); + return; + } + tst_res(critical > 50 ? TPASS : TFAIL, "%d| =:%-4d", i, critical); } @@ -169,5 +223,5 @@ static struct tst_test test = { .test = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 150, + .runtime = 150, }; diff --git a/lib/newlib_tests/tst_fuzzy_sync03.c b/lib/newlib_tests/tst_fuzzy_sync03.c index 47ce7675..7468e79e 100755 --- a/lib/newlib_tests/tst_fuzzy_sync03.c +++ b/lib/newlib_tests/tst_fuzzy_sync03.c @@ -99,5 +99,5 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .test_all = run, - .max_runtime = 150, + .runtime = 150, }; diff --git a/lib/newlib_tests/tst_res_flags.c b/lib/newlib_tests/tst_res_flags.c new file mode 100644 index 00000000..a14f0df2 --- /dev/null +++ b/lib/newlib_tests/tst_res_flags.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Petr Vorel + */ + +/* + * Test tst_res() flags. + */ + +#include "tst_test.h" + +#define FLAG(x) .flag = x, .str = #x +static struct tcase { + int flag; + const char *str; + const char *note; +} tcases[] = { + {FLAG(TPASS)}, + {FLAG(TFAIL)}, + {FLAG(TBROK)}, + {FLAG(TCONF)}, + {FLAG(TWARN)}, + {FLAG(TINFO)}, + {FLAG(TDEBUG), " (printed only with -D or LTP_ENABLE_DEBUG=1)"}, +}; + +static void do_cleanup(void) +{ + tst_brk(TBROK, "TBROK message should be TWARN in cleanup"); +} + +static void do_test(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(tcases); i++) + tst_res(tcases[i].flag, "%s message%s", tcases[i].str, + tcases[i].note ?: ""); +} + +static struct tst_test test = { + .test_all = do_test, + .cleanup = do_cleanup, +}; diff --git a/lib/newlib_tests/tst_safe_sscanf.c b/lib/newlib_tests/tst_safe_sscanf.c new file mode 100644 index 00000000..68f49a87 --- /dev/null +++ b/lib/newlib_tests/tst_safe_sscanf.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Linux Test Project + */ + +/* + * Basic unit test for the tst_safe_sscanf() function. + */ + +#include "tst_test.h" + +static void check_safe_sscanf(void) +{ + int day, month, year; + int nvalues = SAFE_SSCANF("14-07-2023", "%d-%d-%d", &day, &month, &year); + + if (nvalues == 3 && day == 14 && month == 7 && year == 2023) + tst_res(TPASS, "%d values parsed : %d,%d,%d", nvalues, day, month, year); + else + tst_res(TFAIL, "expected 3 values 14,7,2023 got: %d values %d,%d,%d", nvalues, day, month, year); +} + +static struct tst_test test = { + .test_all = check_safe_sscanf, +}; diff --git a/lib/parse_opts.c b/lib/parse_opts.c index a9d50589..03e83331 100755 --- a/lib/parse_opts.c +++ b/lib/parse_opts.c @@ -88,11 +88,6 @@ static struct std_option_t { {"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} }; @@ -118,11 +113,6 @@ static void usc_recressive_func(); #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; @@ -219,11 +209,6 @@ const char *parse_opts(int ac, char **av, const option_t * user_optarr, print_help(uhf); exit(0); break; -#ifdef UCLINUX - case 'C': /* Run child */ - child_args = optarg; - break; -#endif default: /* Check all the user specified options */ @@ -400,7 +385,7 @@ const char *parse_opts(int ac, char **av, const option_t * user_optarr, 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; @@ -410,7 +395,6 @@ const char *parse_opts(int ac, char **av, const option_t * user_optarr, STD_LP_sbrk); } } -#endif /* if !defined(UCLINUX) */ if ((ptr = getenv("USC_LP_RECFUN")) != NULL) { if (sscanf(ptr, "%i", &k) == 1 && k >= 0) { @@ -453,7 +437,6 @@ const char *parse_opts(int ac, char **av, const option_t * user_optarr, ***********************************************************************/ int usc_global_setup_hook(void) { -#ifndef UCLINUX if (STD_TP_sbrk || STD_LP_sbrk) STD_start_break = sbrk(0); /* get original sbreak size */ @@ -462,7 +445,6 @@ int usc_global_setup_hook(void) if (Debug) printf("after sbrk(%d)\n", STD_TP_sbrk); } -#endif return 0; } @@ -538,13 +520,12 @@ int usc_test_looping(int counter) 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; diff --git a/lib/safe_file_ops.c b/lib/safe_file_ops.c index 63ae2dbb..8314c4b1 100755 --- a/lib/safe_file_ops.c +++ b/lib/safe_file_ops.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "test.h" #include "safe_file_ops_fn.h" @@ -182,6 +183,9 @@ int file_lines_scanf(const char *file, const int lineno, fp = fopen(path, "r"); if (fp == NULL) { + if (strict == 0 && errno == ENOENT) + return 1; + tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn, "Failed to open FILE '%s' for reading", path); return 1; diff --git a/lib/safe_macros.c b/lib/safe_macros.c index 951e1b06..a3145b8d 100755 --- a/lib/safe_macros.c +++ b/lib/safe_macros.c @@ -293,7 +293,7 @@ ssize_t safe_read(const char *file, const int lineno, void (*cleanup_fn) (void), rval = read(fildes, buf, nbyte); - if (rval == -1 || (len_strict && (size_t)rval != nbyte)) { + if (rval == -1) { tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn, "read(%d,%p,%zu) failed, returned %zd", fildes, buf, nbyte, rval); @@ -301,6 +301,10 @@ ssize_t safe_read(const char *file, const int lineno, void (*cleanup_fn) (void), tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn, "Invalid read(%d,%p,%zu) return value %zd", fildes, buf, nbyte, rval); + } else if (len_strict && (size_t)rval != nbyte) { + tst_brkm_(file, lineno, TBROK, cleanup_fn, + "Short read(%d,%p,%zu) returned only %zd", + fildes, buf, nbyte, rval); } return rval; @@ -547,6 +551,14 @@ ssize_t safe_write(const char *file, const int lineno, void (cleanup_fn) (void), tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn, "write(%d,%p,%zu) failed", fildes, buf, nbyte); + return rval; + } + + if (rval < 0) { + tst_brkm_(file, lineno, TBROK, cleanup_fn, + "invalid write() return value %zi", + rval); + return rval; } if (len_strict == SAFE_WRITE_ANY) @@ -554,7 +566,7 @@ ssize_t safe_write(const char *file, const int lineno, void (cleanup_fn) (void), if (len_strict == SAFE_WRITE_ALL) { if ((size_t)rval != nbyte) - tst_brkm_(file, lineno, TBROK | TERRNO, + tst_brkm_(file, lineno, TBROK, cleanup_fn, "short write(%d,%p,%zu) " "return value %zd", fildes, buf, nbyte, rval); @@ -763,6 +775,25 @@ int safe_fchown(const char *file, const int lineno, void (cleanup_fn)(void), return rval; } +int safe_lchown(const char *file, const int lineno, void (cleanup_fn)(void), + const char *path, uid_t owner, gid_t group) +{ + int rval; + + rval = lchown(path, owner, group); + + if (rval == -1) { + tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn, + "lchown(%s,%d,%d) failed", path, owner, group); + } else if (rval) { + tst_brkm_(file, lineno, TBROK | TERRNO, cleanup_fn, + "Invalid lchown(%s,%d,%d) return value %d", path, + owner, group, rval); + } + + return rval; +} + pid_t safe_wait(const char *file, const int lineno, void (cleanup_fn)(void), int *status) { @@ -895,11 +926,14 @@ static int possibly_fuse(const char *fs_type) 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) + const void *data, int *is_fuse) { int rval = -1; char mpath[PATH_MAX]; + if (is_fuse) + *is_fuse = 0; + if (realpath(target, mpath)) { tst_resm_(file, lineno, TINFO, "Mounting %s to %s fstyp=%s flags=%lx", @@ -913,7 +947,10 @@ int safe_mount(const char *file, const int lineno, void (*cleanup_fn)(void), * the kernel's NTFS driver doesn't have proper write support. */ if (!filesystemtype || strcmp(filesystemtype, "ntfs")) { + mode_t old_umask = umask(0); + rval = mount(source, target, filesystemtype, mountflags, data); + umask(old_umask); if (!rval) return 0; } @@ -927,14 +964,26 @@ int safe_mount(const char *file, const int lineno, void (*cleanup_fn)(void), */ if (possibly_fuse(filesystemtype)) { char buf[1024]; + const char *mount_ro = ""; + + if (mountflags & MS_RDONLY) + mount_ro = "-o ro"; + + if (mountflags & (~MS_RDONLY)) { + tst_brkm_(file, lineno, TBROK, cleanup_fn, + "FUSE mount flag(s) not implemented!"); + } tst_resm_(file, lineno, TINFO, "Trying FUSE..."); - snprintf(buf, sizeof(buf), "mount.%s '%s' '%s'", - filesystemtype, source, target); + snprintf(buf, sizeof(buf), "mount.%s %s '%s' '%s'", + filesystemtype, mount_ro, source, target); rval = tst_system(buf); - if (WIFEXITED(rval) && WEXITSTATUS(rval) == 0) + if (WIFEXITED(rval) && WEXITSTATUS(rval) == 0) { + if (is_fuse) + *is_fuse = 1; return 0; + } tst_brkm_(file, lineno, TBROK, cleanup_fn, "mount.%s failed with %i", filesystemtype, rval); diff --git a/lib/self_exec.c b/lib/self_exec.c deleted file mode 100755 index de7d0951..00000000 --- a/lib/self_exec.c +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- 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 - * - * 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 -#include -#include -#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/lib/tests/.gitignore b/lib/tests/.gitignore index 1d880c1b..00237f53 100644 --- a/lib/tests/.gitignore +++ b/lib/tests/.gitignore @@ -1,7 +1,4 @@ /tst_tmpdir_test -/tst_checkpoint -/tst_checkpoint_wait_timeout -/tst_checkpoint_wake_timeout /tst_process_state /tst_cleanup_once /tst_safe_macros diff --git a/lib/tests/tst_checkpoint.c b/lib/tests/tst_checkpoint.c deleted file mode 100755 index 2cb17a5f..00000000 --- a/lib/tests/tst_checkpoint.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2012-2015 Cyril Hrubis - * - * 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 - -#include "test.h" - -char *TCID = "tst_checkpoint"; -int TST_TOTAL = 1; - -int main(void) -{ - int pid; - - tst_tmpdir(); - - TST_CHECKPOINT_INIT(tst_rmdir); - - pid = fork(); - - switch (pid) { - case -1: - tst_brkm(TBROK | TERRNO, NULL, "Fork failed"); - break; - case 0: - fprintf(stderr, "Child: checkpoint signaling\n"); - TST_SAFE_CHECKPOINT_WAKE(NULL, 0); - exit(0); - break; - default: - TST_SAFE_CHECKPOINT_WAIT(tst_rmdir, 0); - fprintf(stderr, "Parent: checkpoint reached\n"); - break; - } - - wait(NULL); - tst_rmdir(); - return 0; -} diff --git a/lib/tests/tst_checkpoint_wait_timeout.c b/lib/tests/tst_checkpoint_wait_timeout.c deleted file mode 100755 index c5fae670..00000000 --- a/lib/tests/tst_checkpoint_wait_timeout.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012-2015 Cyril Hrubis - * - * 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 - -#include "test.h" - -char *TCID = "tst_checkpoint_wait_timeout"; -int TST_TOTAL = 1; - -int main(void) -{ - int pid; - - tst_tmpdir(); - - TST_CHECKPOINT_INIT(tst_rmdir); - - pid = fork(); - - switch (pid) { - case -1: - tst_brkm(TBROK | TERRNO, NULL, "Fork failed"); - break; - case 0: - TST_SAFE_CHECKPOINT_WAIT(NULL, 0); - fprintf(stderr, "Child: checkpoint reached\n"); - exit(0); - break; - default: - fprintf(stderr, "Parent: exiting without signaling\n"); - tst_rmdir(); - exit(0); - break; - } - - return 0; -} diff --git a/lib/tests/tst_checkpoint_wake_timeout.c b/lib/tests/tst_checkpoint_wake_timeout.c deleted file mode 100755 index 8af1feb1..00000000 --- a/lib/tests/tst_checkpoint_wake_timeout.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2013 Linux Test Project - * Copyright (C) 2015 Cyril Hrubis - * - * 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 - -#include "test.h" - -char *TCID = "tst_checkpoint_wake_timeout"; -int TST_TOTAL = 1; - -int main(void) -{ - tst_tmpdir(); - - TST_CHECKPOINT_INIT(tst_rmdir); - TST_SAFE_CHECKPOINT_WAKE(tst_rmdir, 0); - fprintf(stderr, "Parent: checkpoint reached\n"); - - return 0; -} diff --git a/lib/tests/tst_strsig.c b/lib/tests/tst_strsig.c index 9a5ca80a..ed5be3f0 100755 --- a/lib/tests/tst_strsig.c +++ b/lib/tests/tst_strsig.c @@ -29,6 +29,7 @@ int TST_TOTAL = 1; int main(void) { + fprintf(stderr, "0 = %s\n", tst_strsig(0)); fprintf(stderr, "SIGKILL = %s\n", tst_strsig(SIGKILL)); fprintf(stderr, "SIGALRM = %s\n", tst_strsig(SIGALRM)); return 0; diff --git a/lib/tlibio.c b/lib/tlibio.c index 2ecdbb42..70e0c6f7 100755 --- a/lib/tlibio.c +++ b/lib/tlibio.c @@ -53,15 +53,6 @@ * 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); * @@ -78,12 +69,9 @@ * */ -#ifdef __linux__ -#ifndef _GNU_SOURCE #define _GNU_SOURCE -#endif #define _LARGEFILE64_SOURCE -#endif + #include "config.h" #include #include @@ -98,38 +86,20 @@ #include #include #include -#ifdef CRAY -#include -#include -#include -#else -/* for linux or sgi */ #include /* readv(2)/writev(2) */ #include -#endif -#if defined(__linux__) || defined(__sun) || defined(__hpux) || defined(_AIX) -#if !defined(UCLINUX) && !defined(__UCLIBC__) -#include -#endif +#ifdef HAVE_AIO_H +# include #endif #include /* atoi, abs */ -#include "tlibio.h" /* defines LIO* marcos */ +#include "tlibio.h" /* defines LIO* macros */ #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 */ @@ -140,7 +110,7 @@ struct lio_info_type Lio_info1[] = { {"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__)) +#ifdef HAVE_AIO_H {"r", LIO_RANDOM | LIO_IO_TYPES | LIO_WAIT_TYPES, "random sync i/o types and wait methods"}, @@ -191,7 +161,7 @@ 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__)) +#ifdef HAVE_AIO_H static volatile int Received_callback = 0; /* number of callbacks received */ static volatile int Rec_callback; #endif @@ -431,7 +401,7 @@ static void lio_async_signal_handler(int sig) return; } -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) +#ifdef HAVE_AIO_H /*********************************************************************** * This is an internal callback handler. * If the handler is called, it will increment the Received_callback @@ -449,7 +419,7 @@ static void lio_async_callback_handler(union sigval sigval) return; } -#endif /* sgi */ +#endif /*********************************************************************** * lio_random_methods @@ -546,23 +516,13 @@ int lio_write_buffer(int fd, /* open file descriptor */ 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__) +#ifdef HAVE_AIO_H 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. */ @@ -580,39 +540,26 @@ int lio_write_buffer(int fd, /* open file descriptor */ *errmsg = Errormsg; Rec_signal = Received_signal; /* get the current number of signals received */ -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) +#ifdef HAVE_AIO_H 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 + +#ifdef HAVE_AIO_H 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) { @@ -646,11 +593,9 @@ int lio_write_buffer(int fd, /* open file descriptor */ return -errno; } } -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) - poffset = (off64_t) ret; -#endif - aiocbp.aio_offset = ret; + poffset = (off64_t) ret; + aiocbp.aio_offset = ret; #endif /* @@ -660,13 +605,11 @@ int lio_write_buffer(int fd, /* open file descriptor */ * the signal. */ if (sig && !(method & LIO_USE_SIGNAL) && !(method & LIO_WAIT_SIGTYPES)) { - sig = 0; /* ignore signal parameter */ } -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) +#ifdef HAVE_AIO_H if (sig && (method & LIO_WAIT_CBTYPES)) sig = 0; /* ignore signal parameter */ -#endif /* * only setup signal hander if sig was specified and @@ -677,26 +620,10 @@ int lio_write_buffer(int fd, /* open file descriptor */ */ 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... @@ -708,6 +635,7 @@ int lio_write_buffer(int fd, /* open file descriptor */ (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. @@ -765,27 +693,7 @@ int lio_write_buffer(int fd, /* open file descriptor */ } 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__)) +#ifdef HAVE_AIO_H sprintf(Lio_SysCall, "aio_write(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig); @@ -811,49 +719,7 @@ int lio_write_buffer(int fd, /* open file descriptor */ } /* 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__)) - +#ifdef HAVE_AIO_H aiocbp.aio_lio_opcode = LIO_WRITE; listio_cmd = LIO_WAIT; io_type = "lio_listio(3) sync write"; @@ -889,40 +755,7 @@ int lio_write_buffer(int fd, /* open file descriptor */ } /* 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__)) +#ifdef HAVE_AIO_H aiocbp.aio_lio_opcode = LIO_WRITE; listio_cmd = LIO_NOWAIT; io_type = "lio_listio(3) async write"; @@ -950,7 +783,6 @@ int lio_write_buffer(int fd, /* open file descriptor */ #endif } /* LIO_IO_ALISTIO */ -#ifndef CRAY else if (method & LIO_IO_SYNCV) { io_type = "writev(2)"; @@ -979,9 +811,7 @@ int lio_write_buffer(int fd, /* open file descriptor */ return ret; } /* LIO_IO_SYNCV */ -#endif - -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) +#ifdef HAVE_AIO_H else if (method & LIO_IO_SYNCP) { io_type = "pwrite(2)"; @@ -1014,7 +844,6 @@ int lio_write_buffer(int fd, /* open file descriptor */ return ret; } /* LIO_IO_SYNCP */ #endif - else { printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, __LINE__); @@ -1024,10 +853,7 @@ int lio_write_buffer(int fd, /* open file descriptor */ /* * wait for async io to complete. */ -#ifdef CRAY - ret = lio_wait4asyncio(method, fd, statptr); -#endif -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) +#ifdef HAVE_AIO_H ret = lio_wait4asyncio(method, fd, &aiocbp); #endif @@ -1055,10 +881,7 @@ int lio_write_buffer(int fd, /* open file descriptor */ * 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__)) +#ifdef HAVE_AIO_H ret = lio_check_asyncio(io_type, size, &aiocbp, method); #endif @@ -1121,19 +944,8 @@ int lio_read_buffer(int fd, /* open file descriptor */ 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__) +#ifdef HAVE_AIO_H 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 */ @@ -1156,39 +968,27 @@ int lio_read_buffer(int fd, /* open file descriptor */ *errmsg = Errormsg; Rec_signal = Received_signal; /* get the current number of signals received */ -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) +#ifdef HAVE_AIO_H 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 + +#ifdef HAVE_AIO_H 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) { @@ -1222,11 +1022,8 @@ int lio_read_buffer(int fd, /* open file descriptor */ return -errno; } } -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) poffset = (off64_t) ret; -#endif aiocbp.aio_offset = ret; - #endif /* @@ -1239,10 +1036,10 @@ int lio_read_buffer(int fd, /* open file descriptor */ sig = 0; /* ignore signal parameter */ } -#if defined(sgi) || (defined(__linux__)&& !defined(__UCLIBC__)) + +#ifdef HAVE_AIO_H if (sig && (method & LIO_WAIT_CBTYPES)) sig = 0; /* ignore signal parameter */ -#endif /* * only setup signal hander if sig was specified and @@ -1251,29 +1048,11 @@ int lio_read_buffer(int fd, /* open file descriptor */ * 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) { + } else if (method & LIO_WAIT_CBTYPES) { aiocbp.aio_sigevent.sigev_notify = SIGEV_THREAD; aiocbp.aio_sigevent.sigev_notify_function = lio_async_callback_handler; @@ -1342,30 +1121,8 @@ int lio_read_buffer(int fd, /* open file descriptor */ 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__)) +#ifdef HAVE_AIO_H + } else if (method & LIO_IO_ASYNC) { sprintf(Lio_SysCall, "aio_read(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig); @@ -1387,51 +1144,9 @@ int lio_read_buffer(int fd, /* open file descriptor */ 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"; @@ -1463,44 +1178,9 @@ int lio_read_buffer(int fd, /* open file descriptor */ 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"; @@ -1525,10 +1205,8 @@ int lio_read_buffer(int fd, /* open file descriptor */ sigrelse(sig); return -errno; } -#endif } /* LIO_IO_ALISTIO */ -#ifndef CRAY else if (method & LIO_IO_SYNCV) { io_type = "readv(2)"; @@ -1557,9 +1235,6 @@ int lio_read_buffer(int fd, /* open file descriptor */ return ret; } /* LIO_IO_SYNCV */ -#endif - -#if defined(sgi) || (defined(__linux__) && !defined(__UCLIBC__)) else if (method & LIO_IO_SYNCP) { io_type = "pread(2)"; @@ -1590,8 +1265,8 @@ int lio_read_buffer(int fd, /* open file descriptor */ __FILE__, __LINE__, ret); return ret; - } /* LIO_IO_SYNCP */ #endif + } else { printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, @@ -1603,10 +1278,7 @@ int lio_read_buffer(int fd, /* open file descriptor */ * 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__)) +#ifdef HAVE_AIO_H ret = lio_wait4asyncio(method, fd, &aiocbp); #endif @@ -1634,17 +1306,14 @@ int lio_read_buffer(int fd, /* open file descriptor */ * 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__)) +#ifdef HAVE_AIO_H ret = lio_check_asyncio(io_type, size, &aiocbp, method); #endif return ret; } /* end of lio_read_buffer */ -#if !defined(__sun) && !defined(__hpux) && !defined(_AIX) +#ifdef HAVE_AIO_H /*********************************************************************** * This function will check that async io was successful. * It can also be used to check sync listio since it uses the @@ -1656,37 +1325,9 @@ int lio_read_buffer(int fd, /* open file descriptor */ * * (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 @@ -1710,10 +1351,6 @@ int lio_check_asyncio(char *io_type, int size, struct aiocb *aiocbp, int 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; @@ -1731,18 +1368,6 @@ int lio_check_asyncio(char *io_type, int size, struct aiocb *aiocbp, int method) 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", @@ -1751,9 +1376,7 @@ int lio_check_asyncio(char *io_type, int size, struct aiocb *aiocbp, int method) return ret; -#endif } /* end of lio_check_asyncio */ -#endif /*********************************************************************** * @@ -1773,45 +1396,20 @@ int lio_check_asyncio(char *io_type, int size, struct aiocb *aiocbp, int method) * * (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", @@ -1819,10 +1417,6 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) (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")); @@ -1843,10 +1437,6 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) 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; @@ -1857,24 +1447,10 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) __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) { @@ -1885,7 +1461,6 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) ++cnt; } -#endif if (Debug_level > 5 && cnt && (cnt % 50) == 0) printf("DEBUG %s/%d: wait active cnt = %d\n", __FILE__, __LINE__, cnt); @@ -1894,25 +1469,12 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) 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 { @@ -1920,14 +1482,9 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) __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) { @@ -1944,10 +1501,6 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) 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) @@ -1960,7 +1513,6 @@ int lio_wait4asyncio(int method, int fd, struct aiocb *aiocbp) } /* end of lio_wait4asyncio */ -#endif /* ifndef linux */ #endif #if UNIT_TEST diff --git a/lib/tst_af_alg.c b/lib/tst_af_alg.c index f5437c5c..a14f9865 100755 --- a/lib/tst_af_alg.c +++ b/lib/tst_af_alg.c @@ -103,6 +103,7 @@ bool tst_have_alg(const char *algtype, const char *algname) case 0: return true; case ENOENT: + case EINVAL: tst_res(TCONF, "kernel doesn't have %s algorithm '%s'", algtype, algname); return false; diff --git a/lib/tst_ansi_color.c b/lib/tst_ansi_color.c index 1c29268f..98041c0a 100755 --- a/lib/tst_ansi_color.c +++ b/lib/tst_ansi_color.c @@ -31,6 +31,9 @@ char* tst_ttype2color(int ttype) case TINFO: return ANSI_COLOR_BLUE; break; + case TDEBUG: + return ANSI_COLOR_WHITE; + break; default: return ""; } diff --git a/lib/tst_buffers.c b/lib/tst_buffers.c index b0bd359e..ac76a784 100755 --- a/lib/tst_buffers.c +++ b/lib/tst_buffers.c @@ -25,6 +25,7 @@ static void setup_canary(struct map *map) for (i = 0; i < map->buf_shift/2; i++) { char c = random(); + buf[map->buf_shift - i - 1] = c; buf[i] = c; } @@ -39,7 +40,7 @@ static void check_canary(struct map *map) 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); + getpid(), (char *)map->addr + map->buf_shift, -i-1); } } } @@ -58,7 +59,7 @@ void *tst_alloc(size_t size) } ret = SAFE_MMAP(NULL, page_size * pages, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); mprotect(ret + (pages-1) * page_size, page_size, PROT_NONE); @@ -83,15 +84,15 @@ char *tst_aprintf(const char *fmt, ...) int len; char *ret; - va_start(va, fmt); - len = vsnprintf(NULL, 0, fmt, va)+1; - va_end(va); + va_start(va, fmt); + len = vsnprintf(NULL, 0, fmt, va)+1; + va_end(va); ret = tst_alloc(len); va_start(va, fmt); - vsprintf(ret, fmt, va); - va_end(va); + vsprintf(ret, fmt, va); + va_end(va); return ret; } @@ -100,7 +101,8 @@ static int count_iovec(int *sizes) { int ret = 0; - while (sizes[ret++] != -1); + while (sizes[ret++] != -1) + ; return ret - 1; } @@ -134,11 +136,11 @@ void tst_buffers_alloc(struct tst_buffers bufs[]) for (i = 0; bufs[i].ptr; i++) { if (bufs[i].size) - *((void**)bufs[i].ptr) = tst_alloc(bufs[i].size); + *((void **)bufs[i].ptr) = tst_alloc(bufs[i].size); else if (bufs[i].iov_sizes) - *((void**)bufs[i].ptr) = tst_iovec_alloc(bufs[i].iov_sizes); + *((void **)bufs[i].ptr) = tst_iovec_alloc(bufs[i].iov_sizes); else - *((void**)bufs[i].ptr) = tst_strdup(bufs[i].str); + *((void **)bufs[i].ptr) = tst_strdup(bufs[i].str); } } @@ -155,6 +157,7 @@ void tst_free_all(void) while (i) { struct map *j = i; + check_canary(i); SAFE_MUNMAP(i->addr, i->size); i = i->next; diff --git a/lib/tst_cgroup.c b/lib/tst_cgroup.c index 5240aada..a37bf151 100755 --- a/lib/tst_cgroup.c +++ b/lib/tst_cgroup.c @@ -15,8 +15,8 @@ #include "tst_test.h" #include "lapi/fcntl.h" #include "lapi/mount.h" -#include "lapi/mkdirat.h" #include "tst_safe_file_at.h" +#include "tst_kconfig.h" struct cgroup_root; @@ -45,7 +45,7 @@ struct cgroup_dir { */ int dir_fd; - int we_created_it:1; + unsigned int we_created_it:1; }; /* The root of a CGroup hierarchy/tree */ @@ -72,9 +72,12 @@ struct cgroup_root { /* CGroup for current test. Which may have children. */ struct cgroup_dir test_dir; - int we_mounted_it:1; + unsigned int nsdelegate:1; + + unsigned int we_mounted_it:1; /* cpuset is in compatability mode */ - int no_cpuset_prefix:1; + unsigned int no_cpuset_prefix:1; + unsigned int memory_recursiveprot:1; }; /* Controller sub-systems */ @@ -82,6 +85,7 @@ enum cgroup_ctrl_indx { CTRL_MEMORY = 1, CTRL_CPU, CTRL_CPUSET, + CTRL_DMEM, CTRL_IO, CTRL_PIDS, CTRL_HUGETLB, @@ -137,7 +141,7 @@ struct cgroup_ctrl { /* Runtime; hierarchy the controller is attached to */ struct cgroup_root *ctrl_root; /* Runtime; whether we required the controller */ - int we_require_it:1; + unsigned int we_require_it:1; }; struct tst_cg_group { @@ -205,6 +209,15 @@ static const struct cgroup_file cpuset_ctrl_files[] = { { } }; +static const struct cgroup_file dmem_ctrl_files[] = { + { "dmem.capacity", NULL, CTRL_DMEM }, + { "dmem.current", NULL, CTRL_DMEM }, + { "dmem.min", NULL, CTRL_DMEM }, + { "dmem.low", NULL, CTRL_DMEM }, + { "dmem.max", NULL, CTRL_DMEM }, + { } +}; + static const struct cgroup_file io_ctrl_files[] = { { "io.stat", NULL, CTRL_IO }, { } @@ -274,6 +287,7 @@ static struct cgroup_ctrl controllers[] = { CGROUP_CTRL_MEMBER(memory, CTRL_MEMORY), CGROUP_CTRL_MEMBER(cpu, CTRL_CPU), CGROUP_CTRL_MEMBER(cpuset, CTRL_CPUSET), + CGROUP_CTRL_MEMBER(dmem, CTRL_DMEM), CGROUP_CTRL_MEMBER(io, CTRL_IO), CGROUP_CTRL_MEMBER(pids, CTRL_PIDS), CGROUP_CTRL_MEMBER(hugetlb, CTRL_HUGETLB), @@ -345,6 +359,11 @@ static int cgroup_v1_mounted(void) return !!roots[1].ver; } +static int cgroup_v2_nsdelegate(void) +{ + return !!roots[0].nsdelegate; +} + static int cgroup_mounted(void) { return cgroup_v2_mounted() || cgroup_v1_mounted(); @@ -362,6 +381,7 @@ static void cgroup_dir_mk(const struct cgroup_dir *const parent, struct cgroup_dir *const new) { const char *dpath; + mode_t old_umask = umask(0); new->dir_root = parent->dir_root; new->dir_name = dir_name; @@ -395,6 +415,7 @@ static void cgroup_dir_mk(const struct cgroup_dir *const parent, opendir: new->dir_fd = SAFE_OPENAT(parent->dir_fd, dir_name, O_PATH | O_DIRECTORY); + umask(old_umask); } #define PATH_MAX_STRLEN 4095 @@ -430,12 +451,28 @@ void tst_cg_print_config(void) } __attribute__ ((nonnull, warn_unused_result)) -static struct cgroup_ctrl *cgroup_find_ctrl(const char *const ctrl_name) +static struct cgroup_ctrl *cgroup_find_ctrl(const char *const ctrl_name, + unsigned int strict) { struct cgroup_ctrl *ctrl; + int l = 0; + char c = ctrl_name[l]; + + while (c == '_' || (c >= 'a' && c <= 'z')) + c = ctrl_name[++l]; + + if (l > 32 && strict) + tst_res(TWARN, "Subsys name len greater than max known value of MAX_CGROUP_TYPE_NAMELEN: %d > 32", l); + + if (!(c == '\n' || c == '\0')) { + if (!strict) + return NULL; + + tst_brk(TBROK, "Unexpected char in %s: %c", ctrl_name, c); + } for_each_ctrl(ctrl) { - if (!strcmp(ctrl_name, ctrl->ctrl_name)) + if (!strncmp(ctrl_name, ctrl->ctrl_name, l)) return ctrl; } @@ -468,7 +505,7 @@ static void cgroup_parse_config_line(const char *const config_entry) if (vars_read != 7) tst_brk(TBROK, "Incorrect number of vars read from config. Config possibly malformed?"); - ctrl = cgroup_find_ctrl(ctrl_name); + ctrl = cgroup_find_ctrl(ctrl_name, 1); if (!ctrl) tst_brk(TBROK, "Could not find ctrl from config. Ctrls changing between calls?"); @@ -551,6 +588,8 @@ static void cgroup_root_scan(const char *const mnt_type, struct cgroup_ctrl *ctrl; uint32_t ctrl_field = 0; int no_prefix = 0; + unsigned int nsdelegate = 0; + unsigned int memory_recursiveprot = 0; char buf[BUFSIZ]; char *tok; const int mnt_dfd = SAFE_OPEN(mnt_dir, O_PATH | O_DIRECTORY); @@ -561,10 +600,14 @@ static void cgroup_root_scan(const char *const mnt_type, SAFE_FILE_READAT(mnt_dfd, "cgroup.controllers", buf, sizeof(buf)); for (tok = strtok(buf, " "); tok; tok = strtok(NULL, " ")) { - const_ctrl = cgroup_find_ctrl(tok); + const_ctrl = cgroup_find_ctrl(tok, 1); if (const_ctrl) add_ctrl(&ctrl_field, const_ctrl); } + for (tok = strtok(mnt_opts, ","); tok; tok = strtok(NULL, ",")) { + nsdelegate |= !strcmp("nsdelegate", tok); + memory_recursiveprot |= !strcmp("memory_recursiveprot", tok); + } if (root->ver && ctrl_field == root->ctrl_field) goto discard; @@ -578,7 +621,7 @@ static void cgroup_root_scan(const char *const mnt_type, v1: for (tok = strtok(mnt_opts, ","); tok; tok = strtok(NULL, ",")) { - const_ctrl = cgroup_find_ctrl(tok); + const_ctrl = cgroup_find_ctrl(tok, 0); if (const_ctrl) add_ctrl(&ctrl_field, const_ctrl); @@ -615,6 +658,8 @@ backref: root->mnt_dir.dir_fd = mnt_dfd; root->ctrl_field = ctrl_field; root->no_cpuset_prefix = no_prefix; + root->nsdelegate = nsdelegate; + root->memory_recursiveprot = memory_recursiveprot; for_each_ctrl(ctrl) { if (has_ctrl(root->ctrl_field, ctrl)) @@ -805,7 +850,7 @@ void tst_cg_require(const char *const ctrl_name, const struct tst_cg_opts *options) { const char *const cgsc = "cgroup.subtree_control"; - struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name); + struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name, 1); struct cgroup_root *root; int base = !strcmp(ctrl->ctrl_name, "base"); @@ -852,6 +897,10 @@ void tst_cg_require(const char *const ctrl_name, mkdirs: root = ctrl->ctrl_root; + + if (options->needs_nsdelegate && cgroup_v2_mounted() && !cgroup_v2_nsdelegate()) + tst_brk(TCONF, "Requires cgroup2 to be mounted with nsdelegate"); + add_ctrl(&root->mnt_dir.ctrl_field, ctrl); if (cgroup_ctrl_on_v2(ctrl) && options->needs_ver == TST_CG_V1) { @@ -1161,7 +1210,7 @@ static const struct cgroup_file *cgroup_file_find(const char *const file, memcpy(ctrl_name, file_name, len); ctrl_name[len] = '\0'; - ctrl = cgroup_find_ctrl(ctrl_name); + ctrl = cgroup_find_ctrl(ctrl_name, 1); if (!ctrl) { tst_brk_(file, lineno, TBROK, @@ -1188,7 +1237,7 @@ enum tst_cg_ver tst_cg_ver(const char *const file, const int lineno, const struct tst_cg_group *const cg, const char *const ctrl_name) { - const struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name); + const struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name, 1); const struct cgroup_dir *dir; if (!strcmp(ctrl_name, "cgroup")) { @@ -1476,3 +1525,21 @@ int safe_cg_occursin(const char *const file, const int lineno, return !!strstr(buf, needle); } + +int tst_cg_memory_recursiveprot(struct tst_cg_group *cg) +{ + if (cg && cg->dirs_by_ctrl[0]->dir_root) + return cg->dirs_by_ctrl[0]->dir_root->memory_recursiveprot; + return 0; +} + +void tst_check_rt_group_sched_support(void) +{ + static const char * const kconf[] = {"CONFIG_RT_GROUP_SCHED=y", NULL}; + + tst_cg_scan(); + + if (cgroup_v2_mounted() && !tst_kconfig_check(kconf)) { + tst_brk(TCONF, "CONFIG_RT_GROUP_SCHED not support on cgroupv2"); + } +} diff --git a/lib/tst_checkpoint.c b/lib/tst_checkpoint.c index 6a294b28..9f803e3e 100755 --- a/lib/tst_checkpoint.c +++ b/lib/tst_checkpoint.c @@ -1,24 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2015 Cyril Hrubis - * - * 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. + * Copyright (c) 2015 Cyril Hrubis + * Copyright (c) Linux Test Project, 2016-2025 */ #include @@ -32,54 +15,15 @@ #define DEFAULT_MSEC_TIMEOUT 10000 +/* + * Global futex array and size for checkpoint synchronization. + * + * NOTE: These are initialized by setup_ipc()/tst_reinit() in tst_test.c + * when .needs_checkpoints is set in the tst_test struct. + */ 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_(file, lineno, TBROK, cleanup_fn, - "checkpoints already initialized"); - 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_(file, lineno, TBROK, cleanup_fn, - "You have to create test temporary directory " - "first (call tst_tmpdir())"); - 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; diff --git a/lib/tst_clocks.c b/lib/tst_clocks.c index 2144a6ae..704ce955 100755 --- a/lib/tst_clocks.c +++ b/lib/tst_clocks.c @@ -11,13 +11,15 @@ #include "tst_clocks.h" #include "lapi/syscalls.h" #include "lapi/posix_clocks.h" +#include "lapi/common_timers.h" +#include "tst_kconfig.h" typedef int (*mysyscall)(clockid_t clk_id, void *ts); int syscall_supported_by_kernel(long sysnr) { int ret; - struct timespec foo; + struct __kernel_timespec foo; ret = syscall(sysnr, 0, &foo); if (ret == -1 && errno == ENOSYS) @@ -144,15 +146,37 @@ const char *tst_clock_name(clockid_t clk_id) } } -time_t tst_get_fs_timestamp(void) +time_t tst_clock_get_timestamp(clockid_t clk_id) { struct timespec ts; int ret; - ret = tst_clock_gettime(CLOCK_REALTIME_COARSE, &ts); + ret = tst_clock_gettime(clk_id, &ts); - if (ret < 0) - tst_brk(TBROK | TERRNO, "clock_gettime(CLOCK_REALTIME_COARSE)"); + if (ret < 0) { + tst_brk(TBROK | TERRNO, "clock_gettime(%s)", + tst_clock_name(clk_id)); + } return ts.tv_sec; } + +time_t tst_fs_timestamp_start(void) +{ + return tst_clock_get_timestamp(CLOCK_REALTIME_COARSE); +} + +time_t tst_fs_timestamp_end(void) +{ + return tst_clock_get_timestamp(CLOCK_REALTIME); +} + +int tst_get_max_clocks(void) +{ + static const char * const kconf_aux[] = {"CONFIG_POSIX_AUX_CLOCKS=y", NULL}; + + if (!tst_kconfig_check(kconf_aux)) + return MAX_CLOCKS + MAX_AUX_CLOCKS; + else + return MAX_CLOCKS; +} diff --git a/lib/tst_clone.c b/lib/tst_clone.c index 2aa00beb..8638052e 100755 --- a/lib/tst_clone.c +++ b/lib/tst_clone.c @@ -14,6 +14,7 @@ pid_t tst_clone(const struct tst_clone_args *tst_args) { struct clone_args args = { .flags = tst_args->flags, + .pidfd = tst_args->pidfd, .exit_signal = tst_args->exit_signal, .cgroup = tst_args->cgroup, }; diff --git a/lib/tst_cmd.c b/lib/tst_cmd.c index b3f8a95a..82d60497 100755 --- a/lib/tst_cmd.c +++ b/lib/tst_cmd.c @@ -34,16 +34,6 @@ #define OPEN_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) #define OPEN_FLAGS (O_WRONLY | O_APPEND | O_CREAT) -enum cmd_op { - OP_GE, /* >= */ - OP_GT, /* > */ - OP_LE, /* <= */ - OP_LT, /* < */ - OP_EQ, /* == */ - OP_NE, /* != */ -}; - - int tst_cmd_fds_(void (cleanup_fn)(void), const char *const argv[], int stdout_fd, @@ -210,7 +200,7 @@ static int mkfs_ext4_version_parser(void) return major * 10000 + minor * 100 + patch; } -static int mkfs_ext4_version_table_get(char *version) +static int mkfs_generic_version_table_get(char *version) { int major, minor, patch; int len; @@ -228,23 +218,44 @@ static int mkfs_ext4_version_table_get(char *version) return major * 10000 + minor * 100 + patch; } +static int mkfs_xfs_version_parser(void) +{ + FILE *f; + int rc, major, minor, patch; + + f = popen("mkfs.xfs -V 2>&1", "r"); + if (!f) { + tst_resm(TWARN, "Could not run mkfs.xfs -V 2>&1 cmd"); + return -1; + } + + rc = fscanf(f, "mkfs.xfs version %d.%d.%d", &major, &minor, &patch); + pclose(f); + if (rc != 3) { + tst_resm(TWARN, "Unable to parse mkfs.xfs version"); + return -1; + } + + return major * 10000 + minor * 100 + patch; +} + static struct version_parser { const char *cmd; int (*parser)(void); int (*table_get)(char *version); } version_parsers[] = { - {"mkfs.ext4", mkfs_ext4_version_parser, mkfs_ext4_version_table_get}, + {"mkfs.ext4", mkfs_ext4_version_parser, mkfs_generic_version_table_get}, + {"mkfs.xfs", mkfs_xfs_version_parser, mkfs_generic_version_table_get}, {}, }; -void tst_check_cmd(const char *cmd) +int tst_check_cmd(const char *cmd, const int brk_nosupp) { struct version_parser *p; char *cmd_token, *op_token, *version_token, *next, *str; char path[PATH_MAX]; char parser_cmd[100]; int ver_parser, ver_get; - int op_flag = 0; strcpy(parser_cmd, cmd); @@ -257,22 +268,7 @@ void tst_check_cmd(const char *cmd) tst_brkm(TCONF, NULL, "Couldn't find '%s' in $PATH", cmd_token); if (!op_token) - return; - - if (!strcmp(op_token, ">=")) - op_flag = OP_GE; - else if (!strcmp(op_token, ">")) - op_flag = OP_GT; - else if (!strcmp(op_token, "<=")) - op_flag = OP_LE; - else if (!strcmp(op_token, "<")) - op_flag = OP_LT; - else if (!strcmp(op_token, "==")) - op_flag = OP_EQ; - else if (!strcmp(op_token, "!=")) - op_flag = OP_NE; - else - tst_brkm(TCONF, NULL, "Invalid op(%s)", op_token); + return 0; if (!version_token || str) { tst_brkm(TCONF, NULL, @@ -300,48 +296,37 @@ void tst_check_cmd(const char *cmd) if (ver_get < 0) tst_brkm(TBROK, NULL, "Failed to get %s version", p->cmd); - switch (op_flag) { - case OP_GE: - if (ver_parser < ver_get) { - tst_brkm(TCONF, NULL, "%s required >= %d, but got %d, " - "the version is required in order run the test.", - cmd, ver_get, ver_parser); - } - break; - case OP_GT: - if (ver_parser <= ver_get) { - tst_brkm(TCONF, NULL, "%s required > %d, but got %d, " - "the version is required in order run the test.", - cmd, ver_get, ver_parser); - } - break; - case OP_LE: - if (ver_parser > ver_get) { - tst_brkm(TCONF, NULL, "%s required <= %d, but got %d, " - "the version is required in order run the test.", - cmd, ver_get, ver_parser); - } - break; - case OP_LT: - if (ver_parser >= ver_get) { - tst_brkm(TCONF, NULL, "%s required < %d, but got %d, " - "the version is required in order run the test.", - cmd, ver_get, ver_parser); - } - break; - case OP_EQ: - if (ver_parser != ver_get) { - tst_brkm(TCONF, NULL, "%s required == %d, but got %d, " - "the version is required in order run the test.", - cmd, ver_get, ver_parser); - } - break; - case OP_NE: - if (ver_parser == ver_get) { - tst_brkm(TCONF, NULL, "%s required != %d, but got %d, " - "the version is required in order run the test.", - cmd, ver_get, ver_parser); - } - break; + if (!strcmp(op_token, ">=")) { + if (ver_parser < ver_get) + goto error; + } else if (!strcmp(op_token, ">")) { + if (ver_parser <= ver_get) + goto error; + } else if (!strcmp(op_token, "<=")) { + if (ver_parser > ver_get) + goto error; + } else if (!strcmp(op_token, "<")) { + if (ver_parser >= ver_get) + goto error; + } else if (!strcmp(op_token, "==")) { + if (ver_parser != ver_get) + goto error; + } else if (!strcmp(op_token, "!=")) { + if (ver_parser == ver_get) + goto error; + } else { + tst_brkm(TCONF, NULL, "Invalid op(%s)", op_token); } + + return 0; +error: + if (brk_nosupp) { + tst_brkm(TCONF, NULL, "%s requires %s %d, but got %d", + cmd, op_token, ver_get, ver_parser); + } else { + tst_resm(TCONF, "%s requires %s %d, but got %d", + cmd, op_token, ver_get, ver_parser); + } + + return 1; } diff --git a/lib/tst_crypto.c b/lib/tst_crypto.c index c01632c2..4495d0ba 100755 --- a/lib/tst_crypto.c +++ b/lib/tst_crypto.c @@ -10,102 +10,42 @@ #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) -{ - const long ret = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CRYPTO); - - if (ret < 0 && errno == EPROTONOSUPPORT) - tst_brk(TCONF | TERRNO, "NETLINK_CRYPTO is probably disabled"); - - if (ret < 0) { - tst_brk(TBROK | TERRNO, - "socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CRYPTO)"); - } - - ses->fd = 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, +int tst_crypto_add_alg(struct tst_netlink_context *ctx, 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); + NETLINK_ADD_MESSAGE(ctx, &nh, alg, sizeof(struct crypto_user_alg)); + return NETLINK_SEND_VALIDATE(ctx) ? 0 : -tst_netlink_errno; } -int tst_crypto_del_alg(struct tst_crypto_session *ses, - const struct crypto_user_alg *alg) +int tst_crypto_del_alg(struct tst_netlink_context *ctx, + const struct crypto_user_alg *alg, unsigned int retries) { - long ret; + int ret; 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), + for (i = 0; i < retries; i++) { + NETLINK_ADD_MESSAGE(ctx, &nh, alg, + sizeof(struct crypto_user_alg)); - SAFE_NETLINK_SEND(ses->fd, &nh, alg); + if (NETLINK_SEND_VALIDATE(ctx)) + return 0; - ret = tst_crypto_recv_ack(ses); - if (ret != -EBUSY || i >= ses->retries) + ret = -tst_netlink_errno; + + if (ret != -EBUSY) break; - if (usleep(1) && errno != EINTR) - tst_brk(TBROK | TERRNO, "usleep(1)"); - - ++i; + usleep(1); } return ret; diff --git a/lib/tst_device.c b/lib/tst_device.c index d659b54c..2364df05 100755 --- a/lib/tst_device.c +++ b/lib/tst_device.c @@ -3,6 +3,7 @@ * Copyright (C) 2014 Cyril Hrubis chrubis@suse.cz */ +#define _GNU_SOURCE #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include "lapi/syscalls.h" #include "test.h" #include "safe_macros.h" @@ -243,17 +245,23 @@ int tst_detach_device_by_fd(const char *dev, int dev_fd) /* keep trying to clear LOOPDEV until we get ENXIO, a quick succession * of attach/detach might not give udev enough time to complete + * + * Since 18048c1af783 ("loop: Fix a race between loop detach and loop open") + * device is detached only after last close. */ for (i = 0; i < 40; i++) { ret = ioctl(dev_fd, LOOP_CLR_FD, 0); - if (ret && (errno == ENXIO)) + if (ret && (errno == ENXIO)) { + SAFE_CLOSE(NULL, dev_fd); return 0; + } if (ret && (errno != EBUSY)) { tst_resm(TWARN, "ioctl(%s, LOOP_CLR_FD, 0) unexpectedly failed with: %s", dev, tst_strerrno(errno)); + SAFE_CLOSE(NULL, dev_fd); return 1; } @@ -262,6 +270,7 @@ int tst_detach_device_by_fd(const char *dev, int dev_fd) tst_resm(TWARN, "ioctl(%s, LOOP_CLR_FD, 0) no ENXIO for too long", dev); + SAFE_CLOSE(NULL, dev_fd); return 1; } @@ -276,7 +285,6 @@ int tst_detach_device(const char *dev) } ret = tst_detach_device_by_fd(dev, dev_fd); - close(dev_fd); return ret; } @@ -421,29 +429,69 @@ int tst_umount(const char *path) return -1; } -int tst_is_mounted(const char *path) +int tst_mount_has_opt(const char *path, const char *opt) { char line[PATH_MAX]; + char abspath[PATH_MAX]; FILE *file; int ret = 0; + if (!realpath(path, abspath)) { + tst_resm(TWARN | TERRNO, "realpath(%s) failed", path); + return 0; + } + file = SAFE_FOPEN(NULL, "/proc/mounts", "r"); while (fgets(line, sizeof(line), file)) { - if (strstr(line, path) != NULL) { + char mount_point[PATH_MAX], options[PATH_MAX]; + + if (sscanf(line, "%*s %s %*s %s", mount_point, options) < 2) + continue; + + if (strcmp(mount_point, abspath) != 0) + continue; + + if (!opt) { ret = 1; break; } + + char *tok = strtok(options, ","); + while (tok) { + if (strcmp(tok, opt) == 0) { + ret = 1; + break; + } + tok = strtok(NULL, ","); + } + if (ret) + break; } SAFE_FCLOSE(NULL, file); + return ret; +} +int tst_is_mounted(const char *path) +{ + int ret = tst_mount_has_opt(path, NULL); if (!ret) tst_resm(TINFO, "No device is mounted at %s", path); return ret; } +int tst_is_mounted_ro(const char *path) +{ + return tst_mount_has_opt(path, "ro"); +} + +int tst_is_mounted_rw(const char *path) +{ + return tst_mount_has_opt(path, "rw"); +} + int tst_is_mounted_at_tmpdir(const char *path) { char cdir[PATH_MAX], mpath[PATH_MAX]; @@ -514,20 +562,189 @@ unsigned long tst_dev_bytes_written(const char *dev) return dev_bytes_written; } +static void btrfs_get_uevent_path(char *tmp_path, char *uevent_path) +{ + int fd; + struct btrfs_ioctl_fs_info_args args = {0}; + char btrfs_uuid_str[UUID_STR_SZ]; + struct dirent *d; + char bdev_path[PATH_MAX]; + DIR *dir; + + tst_resm(TINFO, "Use BTRFS specific strategy"); + + fd = SAFE_OPEN(NULL, tmp_path, O_DIRECTORY); + if (!ioctl(fd, BTRFS_IOC_FS_INFO, &args)) { + sprintf(btrfs_uuid_str, + UUID_FMT, + args.fsid[0], args.fsid[1], + args.fsid[2], args.fsid[3], + args.fsid[4], args.fsid[5], + args.fsid[6], args.fsid[7], + args.fsid[8], args.fsid[9], + args.fsid[10], args.fsid[11], + args.fsid[12], args.fsid[13], + args.fsid[14], args.fsid[15]); + sprintf(bdev_path, + "/sys/fs/btrfs/%s/devices", btrfs_uuid_str); + } else { + tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl on %s failed.", tmp_path); + } + SAFE_CLOSE(NULL, fd); + + dir = SAFE_OPENDIR(NULL, bdev_path); + while ((d = SAFE_READDIR(NULL, dir))) { + if (d->d_name[0] != '.') + break; + } + + uevent_path[0] = '\0'; + + if (d) + sprintf(uevent_path, "%s/%s/uevent", bdev_path, d->d_name); + else + tst_brkm(TBROK | TERRNO, NULL, "No backing device found while looking in %s", bdev_path); + + if (SAFE_READDIR(NULL, dir)) + tst_resm(TINFO, "Warning: used first of multiple backing device."); + + SAFE_CLOSEDIR(NULL, dir); +} + +static char *overlay_mount_from_dev(dev_t dev) +{ + unsigned int dev_major, dev_minor, mnt_major, mnt_minor; + FILE *fp; + char line[4096]; + char *mountpoint; + int ret; + + dev_major = major(dev); + dev_minor = minor(dev); + + fp = SAFE_FOPEN(NULL, "/proc/self/mountinfo", "r"); + while (fgets(line, sizeof(line), fp) != NULL) { + ret = sscanf(line, "%*d %*d %u:%u %*s %ms", + &mnt_major, &mnt_minor, &mountpoint); + if (ret != 3) + tst_brkm(TBROK, NULL, + "failed to parse mountinfo line: '%s'", + line); + if (mnt_major == dev_major && mnt_minor == dev_minor) + break; + free(mountpoint); + mountpoint = NULL; + } + SAFE_FCLOSE(NULL, fp); + if (!mountpoint) + tst_brkm(TBROK, NULL, + "Unable to find mount entry for device %u:%u", + dev_major, dev_minor); + + return mountpoint; +} + +static char *overlay_get_upperdir(char *mountpoint) +{ + FILE *mntf; + struct mntent *mnt; + char *optstr, *optstart, *optend; + char *upperdir = NULL; + + mntf = setmntent("/proc/self/mounts", "r"); + if (!mntf) + tst_brkm(TBROK | TERRNO, NULL, "Can't open /proc/self/mounts"); + + while ((mnt = getmntent(mntf)) != NULL) { + if (strcmp(mnt->mnt_dir, mountpoint)) + continue; + + if (strcmp(mnt->mnt_type, "overlay")) + tst_brkm(TBROK, NULL, + "expected overlayfs on mount point \"%s\", but it is of type %s.", + mountpoint, mnt->mnt_type); + + optstr = hasmntopt(mnt, "upperdir"); + + if (optstr) { + optstart = strchr(optstr, '='); + optstart++; + optend = strchrnul(optstr, ','); + upperdir = strndup(optstart, optend - optstart); + break; + } + + tst_brkm(TBROK, NULL, + "mount point %s does not contain an upperdir", + mountpoint); + } + endmntent(mntf); + + if (!upperdir) + tst_brkm(TBROK, NULL, + "Unable to find mount point \"%s\" in mount table", + mountpoint); + + return upperdir; +} + +/* + * To get from a file or directory on an overlayfs to a device + * for an underlying mountpoint requires the following steps: + * + * 1. stat() the pathname and pick out st_dev. + * 2. use the st_dev to look up the mount point of the file + * system in /proc/self/mountinfo + * + * Because 'mountinfo' is a much more complicated file format than + * 'mounts', we switch to looking up the mount point in /proc/mounts, + * and use the mntent.h helpers to parse the entries. + * + * 3. Using getmntent(), find the entry for the mount point identified + * in step 2. + * 4. Call hasmntopt() to find the upperdir option, and parse that + * option to get to the path name for the upper directory. + * 5. Call stat on the upper directory. This should now contain + * the major and minor number for the underlying device (assuming + * that there aren't nested overlay file systems). + * 6. Populate the uevent path. + * + * Example /proc/self/mountinfo line for overlayfs: + * 471 69 0:48 / /tmp rw,relatime shared:242 - overlay overlay rw,seclabel,lowerdir=/tmp,upperdir=/mnt/upper/upper,workdir=/mnt/upper/work,uuid=null + * + * See section 3.5 of the kernel's Documentation/filesystems/proc.rst file + * for a detailed explanation of the mountinfo format. + */ +static void overlay_get_uevent_path(char *tmp_path, char *uevent_path) +{ + struct stat st; + char *mountpoint, *upperdir; + + tst_resm(TINFO, "Use OVERLAYFS specific strategy"); + + SAFE_STAT(NULL, tmp_path, &st); + + mountpoint = overlay_mount_from_dev(st.st_dev); + upperdir = overlay_get_upperdir(mountpoint); + free(mountpoint); + + SAFE_STAT(NULL, upperdir, &st); + free(upperdir); + + tst_resm(TINFO, "WARNING: used first of multiple backing devices"); + sprintf(uevent_path, "/sys/dev/block/%d:%d/uevent", + major(st.st_dev), minor(st.st_dev)); +} + __attribute__((nonnull)) void tst_find_backing_dev(const char *path, char *dev, size_t dev_size) { struct stat buf; - struct btrfs_ioctl_fs_info_args args = {0}; - struct dirent *d; + struct statfs fsbuf; char uevent_path[PATH_MAX+PATH_MAX+10]; //10 is for the static uevent path char dev_name[NAME_MAX]; - char bdev_path[PATH_MAX]; char tmp_path[PATH_MAX]; - char btrfs_uuid_str[UUID_STR_SZ]; - DIR *dir; unsigned int dev_major, dev_minor; - int fd; if (stat(path, &buf) < 0) tst_brkm(TWARN | TERRNO, NULL, "stat() failed"); @@ -541,50 +758,15 @@ void tst_find_backing_dev(const char *path, char *dev, size_t dev_size) dev_minor = minor(buf.st_dev); *dev = '\0'; - if (dev_major == 0) { - tst_resm(TINFO, "Use BTRFS specific strategy"); + if (statfs(path, &fsbuf) < 0) + tst_brkm(TBROK | TERRNO, NULL, "statfs() failed"); - fd = SAFE_OPEN(NULL, tmp_path, O_DIRECTORY); - if (!ioctl(fd, BTRFS_IOC_FS_INFO, &args)) { - sprintf(btrfs_uuid_str, - UUID_FMT, - args.fsid[0], args.fsid[1], - args.fsid[2], args.fsid[3], - args.fsid[4], args.fsid[5], - args.fsid[6], args.fsid[7], - args.fsid[8], args.fsid[9], - args.fsid[10], args.fsid[11], - args.fsid[12], args.fsid[13], - args.fsid[14], args.fsid[15]); - sprintf(bdev_path, - "/sys/fs/btrfs/%s/devices", btrfs_uuid_str); - } else { - if (errno == ENOTTY) - tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl failed. Is %s on a tmpfs?", path); - - tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl on %s failed.", tmp_path); - } - SAFE_CLOSE(NULL, fd); - - dir = SAFE_OPENDIR(NULL, bdev_path); - while ((d = SAFE_READDIR(NULL, dir))) { - if (d->d_name[0] != '.') - break; - } - - uevent_path[0] = '\0'; - - if (d) { - sprintf(uevent_path, "%s/%s/uevent", - bdev_path, d->d_name); - } else { - tst_brkm(TBROK | TERRNO, NULL, "No backing device found while looking in %s.", bdev_path); - } - - if (SAFE_READDIR(NULL, dir)) - tst_resm(TINFO, "Warning: used first of multiple backing device."); - - SAFE_CLOSEDIR(NULL, dir); + if (fsbuf.f_type == TST_BTRFS_MAGIC) { + btrfs_get_uevent_path(tmp_path, uevent_path); + } else if (fsbuf.f_type == TST_OVERLAYFS_MAGIC) { + overlay_get_uevent_path(tmp_path, uevent_path); + } else if (dev_major == 0) { + tst_brkm(TBROK, NULL, "%s resides on an unsupported pseudo-file system", path); } else { tst_resm(TINFO, "Use uevent strategy"); sprintf(uevent_path, diff --git a/lib/tst_fd.c b/lib/tst_fd.c new file mode 100644 index 00000000..6538a098 --- /dev/null +++ b/lib/tst_fd.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2023 Cyril Hrubis + */ + +#define TST_NO_DEFAULT_MAIN + +#include +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_macros.h" + +#include "lapi/pidfd.h" +#include "lapi/io_uring.h" +#include "lapi/bpf.h" +#include "lapi/fsmount.h" + +#include "tst_fd.h" + +struct tst_fd_desc { + void (*open_fd)(struct tst_fd *fd); + void (*destroy)(struct tst_fd *fd); + const char *desc; +}; + +static void open_file(struct tst_fd *fd) +{ + fd->fd = SAFE_OPEN("fd_file", O_RDWR | O_CREAT, 0666); + SAFE_UNLINK("fd_file"); +} + +static void open_path(struct tst_fd *fd) +{ + int tfd; + + tfd = SAFE_CREAT("fd_file", 0666); + SAFE_CLOSE(tfd); + + fd->fd = SAFE_OPEN("fd_file", O_PATH); + + SAFE_UNLINK("fd_file"); +} + +static void open_dir(struct tst_fd *fd) +{ + SAFE_MKDIR("fd_dir", 0700); + fd->fd = SAFE_OPEN("fd_dir", O_DIRECTORY); + SAFE_RMDIR("fd_dir"); +} + +static void open_dev_zero(struct tst_fd *fd) +{ + fd->fd = SAFE_OPEN("/dev/zero", O_RDONLY); +} + +static void open_proc_self_maps(struct tst_fd *fd) +{ + fd->fd = SAFE_OPEN("/proc/self/maps", O_RDONLY); +} + +static void open_pipe_read(struct tst_fd *fd) +{ + int pipe[2]; + + SAFE_PIPE(pipe); + fd->fd = pipe[0]; + fd->priv = pipe[1]; +} + +static void open_pipe_write(struct tst_fd *fd) +{ + int pipe[2]; + + SAFE_PIPE(pipe); + fd->fd = pipe[1]; + fd->priv = pipe[0]; +} + +static void destroy_pipe(struct tst_fd *fd) +{ + SAFE_CLOSE(fd->priv); +} + +static void open_unix_sock(struct tst_fd *fd) +{ + fd->fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); +} + +static void open_inet_sock(struct tst_fd *fd) +{ + fd->fd = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); +} + +static void open_epoll(struct tst_fd *fd) +{ + fd->fd = epoll_create(1); + + if (fd->fd < 0) + tst_res(TCONF | TERRNO, "epoll_create()"); +} + +static void open_eventfd(struct tst_fd *fd) +{ + fd->fd = eventfd(0, 0); + + if (fd->fd < 0) + tst_res(TCONF | TERRNO, "Skipping %s", tst_fd_desc(fd)); +} + +static void open_signalfd(struct tst_fd *fd) +{ + sigset_t sfd_mask; + + sigemptyset(&sfd_mask); + + fd->fd = signalfd(-1, &sfd_mask, 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_timerfd(struct tst_fd *fd) +{ + fd->fd = timerfd_create(CLOCK_REALTIME, 0); + + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_pidfd(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_pidfd_open, getpid(), 0); + if (fd->fd < 0) + tst_res(TCONF | TERRNO, "pidfd_open()"); +} + +static void open_fanotify(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_fanotify_init, FAN_CLASS_NOTIF, O_RDONLY); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_inotify(struct tst_fd *fd) +{ + fd->fd = inotify_init(); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_userfaultfd(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_userfaultfd, 0); + + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_perf_event(struct tst_fd *fd) +{ + struct perf_event_attr pe_attr = { + .type = PERF_TYPE_SOFTWARE, + .size = sizeof(struct perf_event_attr), + .config = PERF_COUNT_SW_CPU_CLOCK, + .disabled = 1, + .exclude_kernel = 1, + .exclude_hv = 1, + }; + + fd->fd = syscall(__NR_perf_event_open, &pe_attr, 0, -1, -1, 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_io_uring(struct tst_fd *fd) +{ + struct io_uring_params uring_params = {}; + + fd->fd = syscall(__NR_io_uring_setup, 1, &uring_params); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_bpf_map(struct tst_fd *fd) +{ + union bpf_attr array_attr = { + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = 4, + .value_size = 8, + .max_entries = 1, + }; + + fd->fd = syscall(__NR_bpf, BPF_MAP_CREATE, &array_attr, sizeof(array_attr)); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_fsopen(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_fsopen, "ext2", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_fspick(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_fspick, AT_FDCWD, "/", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_open_tree(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_open_tree, AT_FDCWD, "/", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_memfd(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_memfd_create, "ltp_memfd", 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static void open_memfd_secret(struct tst_fd *fd) +{ + fd->fd = syscall(__NR_memfd_secret, 0); + if (fd->fd < 0) { + tst_res(TCONF | TERRNO, + "Skipping %s", tst_fd_desc(fd)); + } +} + +static struct tst_fd_desc fd_desc[] = { + [TST_FD_FILE] = {.open_fd = open_file, .desc = "file"}, + [TST_FD_PATH] = {.open_fd = open_path, .desc = "O_PATH file"}, + [TST_FD_DIR] = {.open_fd = open_dir, .desc = "directory"}, + [TST_FD_DEV_ZERO] = {.open_fd = open_dev_zero, .desc = "/dev/zero"}, + [TST_FD_PROC_MAPS] = {.open_fd = open_proc_self_maps, .desc = "/proc/self/maps"}, + [TST_FD_PIPE_READ] = {.open_fd = open_pipe_read, .desc = "pipe read end", .destroy = destroy_pipe}, + [TST_FD_PIPE_WRITE] = {.open_fd = open_pipe_write, .desc = "pipe write end", .destroy = destroy_pipe}, + [TST_FD_UNIX_SOCK] = {.open_fd = open_unix_sock, .desc = "unix socket"}, + [TST_FD_INET_SOCK] = {.open_fd = open_inet_sock, .desc = "inet socket"}, + [TST_FD_EPOLL] = {.open_fd = open_epoll, .desc = "epoll"}, + [TST_FD_EVENTFD] = {.open_fd = open_eventfd, .desc = "eventfd"}, + [TST_FD_SIGNALFD] = {.open_fd = open_signalfd, .desc = "signalfd"}, + [TST_FD_TIMERFD] = {.open_fd = open_timerfd, .desc = "timerfd"}, + [TST_FD_PIDFD] = {.open_fd = open_pidfd, .desc = "pidfd"}, + [TST_FD_FANOTIFY] = {.open_fd = open_fanotify, .desc = "fanotify"}, + [TST_FD_INOTIFY] = {.open_fd = open_inotify, .desc = "inotify"}, + [TST_FD_USERFAULTFD] = {.open_fd = open_userfaultfd, .desc = "userfaultfd"}, + [TST_FD_PERF_EVENT] = {.open_fd = open_perf_event, .desc = "perf event"}, + [TST_FD_IO_URING] = {.open_fd = open_io_uring, .desc = "io uring"}, + [TST_FD_BPF_MAP] = {.open_fd = open_bpf_map, .desc = "bpf map"}, + [TST_FD_FSOPEN] = {.open_fd = open_fsopen, .desc = "fsopen"}, + [TST_FD_FSPICK] = {.open_fd = open_fspick, .desc = "fspick"}, + [TST_FD_OPEN_TREE] = {.open_fd = open_open_tree, .desc = "open_tree"}, + [TST_FD_MEMFD] = {.open_fd = open_memfd, .desc = "memfd"}, + [TST_FD_MEMFD_SECRET] = {.open_fd = open_memfd_secret, .desc = "memfd secret"}, +}; + +const char *tst_fd_desc(struct tst_fd *fd) +{ + if (fd->type >= ARRAY_SIZE(fd_desc)) + return "invalid"; + + return fd_desc[fd->type].desc; +} + +int tst_fd_next(struct tst_fd *fd) +{ + size_t len = ARRAY_SIZE(fd_desc); + + if (fd->fd >= 0) { + SAFE_CLOSE(fd->fd); + + if (fd_desc[fd->type].destroy) + fd_desc[fd->type].destroy(fd); + + fd->type++; + } + + for (;;) { + if (fd->type >= len) + return 0; + + fd_desc[fd->type].open_fd(fd); + + if (fd->fd >= 0) + return 1; + + fd->type++; + } +} diff --git a/lib/tst_fill_file.c b/lib/tst_fill_file.c index 80472007..6cedde73 100755 --- a/lib/tst_fill_file.c +++ b/lib/tst_fill_file.c @@ -1,34 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* + * Copyright (c) Linux Test Project, 2014-2024 * 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 - * */ #define _GNU_SOURCE -#include #include #include -#include -#include -#include #include "lapi/fallocate.h" - -#include "test.h" +#include "tst_fs.h" int tst_fill_fd(int fd, char pattern, size_t bs, size_t bcount) { diff --git a/lib/tst_fill_fs.c b/lib/tst_fill_fs.c index 5e8cf919..c62d48e2 100755 --- a/lib/tst_fill_fs.c +++ b/lib/tst_fill_fs.c @@ -16,7 +16,7 @@ #include "tst_rand_data.h" #include "tst_safe_file_at.h" -void fill_random(const char *path, int verbose) +static void fill_random(const char *path, int verbose) { int i = 0; char file[PATH_MAX]; @@ -71,7 +71,7 @@ void fill_random(const char *path, int verbose) } } -void fill_flat_vec(const char *path, int verbose) +static void fill_flat_vec(const char *path, int verbose) { int dir, fd; struct iovec iov[512]; diff --git a/lib/tst_fips.c b/lib/tst_fips.c deleted file mode 100755 index 82dafef7..00000000 --- a/lib/tst_fips.c +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2021 Petr Vorel - */ - -#define TST_NO_DEFAULT_MAIN - -#define PATH_FIPS "/proc/sys/crypto/fips_enabled" - -#include "tst_test.h" -#include "tst_safe_macros.h" -#include "tst_fips.h" - -int tst_fips_enabled(void) -{ - int fips = 0; - - if (access(PATH_FIPS, R_OK) == 0) { - SAFE_FILE_SCANF(PATH_FIPS, "%d", &fips); - } - - tst_res(TINFO, "FIPS: %s", fips ? "on" : "off"); - return fips; -} diff --git a/lib/tst_fs_has_free.c b/lib/tst_fs_has_free.c index e82dfa83..080d693a 100755 --- a/lib/tst_fs_has_free.c +++ b/lib/tst_fs_has_free.c @@ -27,7 +27,7 @@ #include "tst_fs.h" int tst_fs_has_free_(void (*cleanup)(void), const char *path, - unsigned int size, unsigned int mult) + uint64_t size, unsigned int mult) { struct statfs sf; diff --git a/lib/tst_fs_setup.c b/lib/tst_fs_setup.c index aaa8f3bc..d3284a14 100755 --- a/lib/tst_fs_setup.c +++ b/lib/tst_fs_setup.c @@ -11,9 +11,10 @@ #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) +void tst_create_overlay_dirs(void) { DIR *dir = opendir(OVL_LOWER); + if (dir == NULL) { SAFE_MKDIR(OVL_LOWER, 0755); SAFE_MKDIR(OVL_UPPER, 0755); @@ -24,11 +25,11 @@ void create_overlay_dirs(void) closedir(dir); } -int mount_overlay(const char *file, const int lineno, int strict) +int tst_mount_overlay(const char *file, const int lineno, int strict) { int ret; - create_overlay_dirs(); + tst_create_overlay_dirs(); ret = mount("overlay", OVL_MNT, "overlay", 0, TST_FS_SETUP_OVERLAYFS_CONFIG); if (ret == 0) diff --git a/lib/tst_fs_type.c b/lib/tst_fs_type.c index d9c9c081..e9efb89d 100755 --- a/lib/tst_fs_type.c +++ b/lib/tst_fs_type.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2005-2021 Linux Test Project * @@ -36,6 +36,8 @@ const char *tst_fs_type_name(long f_type) return "9p"; case TST_RAMFS_MAGIC: return "ramfs"; + case TST_BCACHE_MAGIC: + return "bcachefs"; case TST_BTRFS_MAGIC: return "btrfs"; case TST_XFS_MAGIC: diff --git a/lib/tst_ioctl.c b/lib/tst_ioctl.c index 364220bc..985cd532 100755 --- a/lib/tst_ioctl.c +++ b/lib/tst_ioctl.c @@ -1,37 +1,30 @@ // SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Linux Test Project, 2019-2023 + */ -#include -#include -#include #include #include #define TST_NO_DEFAULT_MAIN #include "tst_test.h" +#include "tst_fs.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; - } + fd = SAFE_OPEN(filename, O_RDWR | O_CREAT, 0666); if (ioctl(fd, FIBMAP, &block)) { tst_res(TINFO | TERRNO, "FIBMAP ioctl is NOT supported"); - close(fd); + SAFE_CLOSE(fd); return 1; } - tst_res(TINFO, "FIBMAP ioctl is supported"); - if (close(fd)) { - tst_res(TWARN | TERRNO, "close(fd) failed"); - return -1; - } + tst_res(TINFO, "FIBMAP ioctl is supported"); + SAFE_CLOSE(fd); + return 0; } diff --git a/lib/tst_kconfig.c b/lib/tst_kconfig.c index 595ea4b0..9bcd5772 100755 --- a/lib/tst_kconfig.c +++ b/lib/tst_kconfig.c @@ -14,6 +14,7 @@ #include "tst_private.h" #include "tst_kconfig.h" #include "tst_bool_expr.h" +#include "tst_safe_stdio.h" static int kconfig_skip_check(void) { @@ -565,3 +566,121 @@ char tst_kconfig_get(const char *confname) return var.choice; } + +void tst_kcmdline_parse(struct tst_kcmdline_var params[], size_t params_len) +{ + char buf[256], line[1024]; + size_t b_pos = 0,l_pos =0, i; + int var_id = -1; + + FILE *f = SAFE_FOPEN("/proc/cmdline", "r"); + + if (fgets(line, sizeof(line), f) == NULL) { + SAFE_FCLOSE(f); + tst_brk(TBROK, "Failed to read /proc/cmdline"); + } + + for (l_pos = 0; line[l_pos] != '\0'; l_pos++) { + char c = line[l_pos]; + + switch (c) { + case '=': + buf[b_pos] = '\0'; + for (i = 0; i < params_len; i++) { + if (strcmp(buf, params[i].key) == 0) { + var_id = (int)i; + params[i].found = true; + } + } + + b_pos = 0; + break; + case ' ': + case '\n': + buf[b_pos] = '\0'; + if (var_id >= 0 && var_id < (int)params_len) + strcpy(params[var_id].value, buf); + + var_id = -1; + b_pos = 0; + break; + default: + if (b_pos + 1 >= sizeof(buf)) { + tst_res(TINFO, "WARNING: Buffer overflowed while parsing /proc/cmdline"); + while (line[l_pos] != '\0' && line[l_pos] != ' ' && line[l_pos] != '\n') + l_pos++; + + var_id = -1; + b_pos = 0; + + if (line[l_pos] != '\0') + l_pos--; + } else { + buf[b_pos++] = c; + } + break; + } + } + + for (i = 0; i < params_len; i++) { + if (params[i].found) + tst_res(TINFO, "%s is found in /proc/cmdline", params[i].key); + else + tst_res(TINFO, "%s is not found in /proc/cmdline", params[i].key); + } + + SAFE_FCLOSE(f); +} + +/* + * List of kernel config options that may degrade performance when enabled. + */ +static struct tst_kconfig_var slow_kconfigs[] = { + TST_KCONFIG_INIT("CONFIG_PROVE_LOCKING"), + TST_KCONFIG_INIT("CONFIG_LOCKDEP"), + TST_KCONFIG_INIT("CONFIG_DEBUG_SPINLOCK"), + TST_KCONFIG_INIT("CONFIG_DEBUG_RT_MUTEXES"), + TST_KCONFIG_INIT("CONFIG_DEBUG_MUTEXES"), + TST_KCONFIG_INIT("CONFIG_KASAN"), + TST_KCONFIG_INIT("CONFIG_SLUB_RCU_DEBUG"), + TST_KCONFIG_INIT("CONFIG_TRACE_IRQFLAGS"), + TST_KCONFIG_INIT("CONFIG_DEBUG_NET"), + TST_KCONFIG_INIT("CONFIG_EXT4_DEBUG"), + TST_KCONFIG_INIT("CONFIG_QUOTA_DEBUG"), + TST_KCONFIG_INIT("CONFIG_FAULT_INJECTION"), + TST_KCONFIG_INIT("CONFIG_DEBUG_OBJECTS") +}; + +static bool slow_kconfig_cached; +static bool slow_kconfig_result; + +int tst_has_slow_kconfig(void) +{ + unsigned int i; + char path_buf[1024]; + + if (slow_kconfig_cached) + return slow_kconfig_result; + + slow_kconfig_cached = 1; + + if (!kconfig_path(path_buf, sizeof(path_buf))) { + slow_kconfig_result = 0; + return 0; + } + + tst_kconfig_read(slow_kconfigs, ARRAY_SIZE(slow_kconfigs)); + + for (i = 0; i < ARRAY_SIZE(slow_kconfigs); i++) { + if (slow_kconfigs[i].choice == 'y') { + tst_res(TINFO, + "%s kernel option detected which might slow the execution", + slow_kconfigs[i].id); + slow_kconfig_result = 1; + return 1; + } + } + + slow_kconfig_result = 0; + return 0; +} diff --git a/lib/tst_kernel.c b/lib/tst_kernel.c index 4b75cead..9ab02e5d 100755 --- a/lib/tst_kernel.c +++ b/lib/tst_kernel.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2017 Cyril Hrubis * Copyright (c) 2020-2021 Petr Vorel + * Copyright (c) Linux Test Project, 2017-2024 * * 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 @@ -18,11 +19,13 @@ #include #include +#include #include #include "test.h" #include "tst_kernel.h" #include "old_safe_stdio.h" +#include "lapi/abisize.h" static int get_kernel_bits_from_uname(struct utsname *buf) { @@ -90,6 +93,19 @@ int tst_kernel_bits(void) return kernel_bits; } +int tst_is_compat_mode(void) +{ + return TST_ABI != tst_kernel_bits(); +} + +bool tst_abi_bits(int abi) +{ + if (abi != 32 && abi != 64) + tst_brkm(TBROK | TERRNO, NULL, "abi parameter can be only 32 or 64"); + + return abi == TST_ABI; +} + static int tst_search_driver_(const char *driver, const char *file) { struct stat st; @@ -193,8 +209,18 @@ int tst_check_builtin_driver(const char *driver) int tst_check_driver(const char *driver) { if (!tst_search_driver(driver, "modules.dep") || - !tst_search_driver(driver, "modules.builtin")) + !tst_check_builtin_driver(driver)) return 0; return -1; } + +int tst_check_preempt_rt(void) +{ + struct utsname uval; + + uname(&uval); + if (strstr(uval.version, "PREEMPT_RT")) + return 1; + return 0; +} diff --git a/lib/tst_kvercmp.c b/lib/tst_kvercmp.c index 552920fa..9e1a511a 100755 --- a/lib/tst_kvercmp.c +++ b/lib/tst_kvercmp.c @@ -92,8 +92,8 @@ int tst_kvcmp(const char *cur_kver, int r1, int r2, int r3) cur_kver); } - testver = (r1 << 16) + (r2 << 8) + r3; - currver = (a1 << 16) + (a2 << 8) + a3; + testver = (r1 << 20) + (r2 << 10) + r3; + currver = (a1 << 20) + (a2 << 10) + a3; return currver - testver; } diff --git a/lib/tst_memutils.c b/lib/tst_memutils.c index c5382ff1..e49684ba 100755 --- a/lib/tst_memutils.c +++ b/lib/tst_memutils.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 SUSE LLC + * Copyright (c) Linux Test Project, 2021-2023 */ #include @@ -11,7 +12,9 @@ #define TST_NO_DEFAULT_MAIN #include "tst_test.h" +#include "tst_memutils.h" #include "tst_capability.h" +#include "tst_safe_stdio.h" #include "lapi/syscalls.h" #define BLOCKSIZE (16 * 1024 * 1024) @@ -182,3 +185,33 @@ void tst_disable_oom_protection(pid_t pid) { set_oom_score_adj(pid, 0); } + +int tst_mapping_in_range(unsigned long low, unsigned long high) +{ + FILE *fp; + + fp = SAFE_FOPEN("/proc/self/maps", "r"); + + while (!feof(fp)) { + unsigned long start, end; + int ret; + + ret = fscanf(fp, "%lx-%lx %*[^\n]\n", &start, &end); + if (ret != 2) { + fclose(fp); + tst_brk(TBROK | TERRNO, "Couldn't parse /proc/self/maps line."); + } + + if ((start >= low) && (start < high)) { + fclose(fp); + return 1; + } + if ((end >= low) && (end < high)) { + fclose(fp); + return 1; + } + } + + fclose(fp); + return 0; +} diff --git a/lib/tst_mkfs.c b/lib/tst_mkfs.c index 736324f0..19d995df 100755 --- a/lib/tst_mkfs.c +++ b/lib/tst_mkfs.c @@ -107,6 +107,9 @@ void tst_mkfs_(const char *file, const int lineno, void (cleanup_fn)(void), "%s not found in $PATH", mkfs); break; default: + tst_resm_(file, lineno, TWARN, + "mkfs may have failed because the device is busy (e.g., udisks2 probing). " + "Consider disabling background probing services."); tst_brkm_(file, lineno, TBROK, cleanup_fn, "%s failed with exit code %i", mkfs, ret); } diff --git a/lib/tst_module.c b/lib/tst_module.c index 9bd44362..42d63ede 100755 --- a/lib/tst_module.c +++ b/lib/tst_module.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * Copyright (c) Linux Test Project, 2016-2024 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,8 +24,10 @@ #include #include #include +#include #include "test.h" +#include "tst_kconfig.h" #include "ltp_priv.h" #include "old_module.h" @@ -122,3 +125,52 @@ void tst_module_unload_(void (cleanup_fn)(void), const char *mod_name) "could not unload %s module", mod_name); } } + +bool tst_module_signature_enforced_(void) +{ + struct tst_kcmdline_var params = TST_KCMDLINE_INIT("module.sig_enforce"); + struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_MODULE_SIG_FORCE"); + int rc; + + tst_kcmdline_parse(¶ms, 1); + tst_kconfig_read(&kconfig, 1); + + rc = params.found || kconfig.choice == 'y'; + tst_resm(TINFO, "module signature enforcement: %s", rc ? "on" : "off"); + + return rc; +} + +void tst_requires_module_signature_disabled_(void) +{ + if (tst_module_signature_enforced_()) + tst_brkm(TCONF, NULL, "module signature is enforced, skip test"); +} + +void tst_modprobe(const char *mod_name, char *const argv[]) +{ + const int offset = 2; /* command name & module path */ + int i, 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] = "modprobe"; + mod_argv[1] = mod_name; + + for (i = offset; i < size; ++i) + mod_argv[i] = argv[i - offset]; + + tst_cmd(NULL, mod_argv, NULL, NULL, 0); +} + +void tst_module_reload(const char *mod_name, char *const argv[]) +{ + tst_resm(TINFO, "Reloading kernel module %s", mod_name); + tst_module_unload_(NULL, mod_name); + tst_modprobe(mod_name, argv); +} diff --git a/lib/tst_netdevice.c b/lib/tst_netdevice.c index dba44c62..1042466b 100755 --- a/lib/tst_netdevice.c +++ b/lib/tst_netdevice.c @@ -12,26 +12,27 @@ #define TST_NO_DEFAULT_MAIN #include "tst_test.h" -#include "tst_rtnetlink.h" +#include "tst_netlink.h" #include "tst_netdevice.h" -static struct tst_rtnl_context *create_request(const char *file, +static struct tst_netlink_context *create_request(const char *file, const int lineno, unsigned int type, unsigned int flags, const void *payload, size_t psize) { - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; struct nlmsghdr header = { .nlmsg_type = type, .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags, }; - ctx = tst_rtnl_create_context(file, lineno); + ctx = tst_netlink_create_context(file, lineno, NETLINK_ROUTE); if (!ctx) return NULL; - if (!tst_rtnl_add_message(file, lineno, ctx, &header, payload, psize)) { - tst_rtnl_destroy_context(file, lineno, ctx); + if (!tst_netlink_add_message(file, lineno, ctx, &header, payload, + psize)) { + tst_netlink_destroy_context(file, lineno, ctx); return NULL; } @@ -103,18 +104,18 @@ int tst_create_veth_pair(const char *file, const int lineno, int strict, { int ret; struct ifinfomsg info = { .ifi_family = AF_UNSPEC }; - struct tst_rtnl_context *ctx; - struct tst_rtnl_attr_list peerinfo[] = { + struct tst_netlink_context *ctx; + struct tst_netlink_attr_list peerinfo[] = { {IFLA_IFNAME, ifname2, strlen(ifname2) + 1, NULL}, {0, NULL, -1, NULL} }; - struct tst_rtnl_attr_list peerdata[] = { + struct tst_netlink_attr_list peerdata[] = { {VETH_INFO_PEER, &info, sizeof(info), peerinfo}, {0, NULL, -1, NULL} }; - struct tst_rtnl_attr_list attrs[] = { + struct tst_netlink_attr_list attrs[] = { {IFLA_IFNAME, ifname1, strlen(ifname1) + 1, NULL}, - {IFLA_LINKINFO, NULL, 0, (const struct tst_rtnl_attr_list[]){ + {IFLA_LINKINFO, NULL, 0, (const struct tst_netlink_attr_list[]){ {IFLA_INFO_KIND, "veth", 4, NULL}, {IFLA_INFO_DATA, NULL, 0, peerdata}, {0, NULL, -1, NULL} @@ -141,17 +142,17 @@ int tst_create_veth_pair(const char *file, const int lineno, int strict, return 0; if (tst_rtnl_add_attr_list(file, lineno, ctx, attrs) != 2) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } - ret = tst_rtnl_send_validate(file, lineno, ctx); - tst_rtnl_destroy_context(file, lineno, ctx); + ret = tst_netlink_send_validate(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); if (strict && !ret) { tst_brk_(file, lineno, TBROK, "Failed to create veth interfaces %s+%s: %s", ifname1, - ifname2, tst_strerrno(tst_rtnl_errno)); + ifname2, tst_strerrno(tst_netlink_errno)); } return ret; @@ -162,10 +163,10 @@ int tst_netdev_add_device(const char *file, const int lineno, int strict, { int ret; struct ifinfomsg info = { .ifi_family = AF_UNSPEC }; - struct tst_rtnl_context *ctx; - struct tst_rtnl_attr_list attrs[] = { + struct tst_netlink_context *ctx; + struct tst_netlink_attr_list attrs[] = { {IFLA_IFNAME, ifname, strlen(ifname) + 1, NULL}, - {IFLA_LINKINFO, NULL, 0, (const struct tst_rtnl_attr_list[]){ + {IFLA_LINKINFO, NULL, 0, (const struct tst_netlink_attr_list[]){ {IFLA_INFO_KIND, devtype, strlen(devtype), NULL}, {0, NULL, -1, NULL} }}, @@ -185,17 +186,17 @@ int tst_netdev_add_device(const char *file, const int lineno, int strict, return 0; if (tst_rtnl_add_attr_list(file, lineno, ctx, attrs) != 2) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } - ret = tst_rtnl_send_validate(file, lineno, ctx); - tst_rtnl_destroy_context(file, lineno, ctx); + ret = tst_netlink_send_validate(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); if (strict && !ret) { tst_brk_(file, lineno, TBROK, "Failed to create %s device %s: %s", devtype, ifname, - tst_strerrno(tst_rtnl_errno)); + tst_strerrno(tst_netlink_errno)); } return ret; @@ -205,7 +206,7 @@ int tst_netdev_remove_device(const char *file, const int lineno, int strict, const char *ifname) { struct ifinfomsg info = { .ifi_family = AF_UNSPEC }; - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; int ret; if (strlen(ifname) >= IFNAMSIZ) { @@ -220,17 +221,17 @@ int tst_netdev_remove_device(const char *file, const int lineno, int strict, return 0; if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } - ret = tst_rtnl_send_validate(file, lineno, ctx); - tst_rtnl_destroy_context(file, lineno, ctx); + ret = tst_netlink_send_validate(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); if (strict && !ret) { tst_brk_(file, lineno, TBROK, "Failed to remove netdevice %s: %s", ifname, - tst_strerrno(tst_rtnl_errno)); + tst_strerrno(tst_netlink_errno)); } return ret; @@ -241,7 +242,7 @@ static int modify_address(const char *file, const int lineno, int strict, unsigned int family, const void *address, unsigned int prefix, size_t addrlen, uint32_t addr_flags) { - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; int index, ret; struct ifaddrmsg info = { .ifa_family = family, @@ -264,23 +265,23 @@ static int modify_address(const char *file, const int lineno, int strict, if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_FLAGS, &addr_flags, sizeof(uint32_t))) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_LOCAL, address, addrlen)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } - ret = tst_rtnl_send_validate(file, lineno, ctx); - tst_rtnl_destroy_context(file, lineno, ctx); + ret = tst_netlink_send_validate(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); if (strict && !ret) { tst_brk_(file, lineno, TBROK, "Failed to modify %s network address: %s", ifname, - tst_strerrno(tst_rtnl_errno)); + tst_strerrno(tst_netlink_errno)); } return ret; @@ -322,7 +323,7 @@ static int change_ns(const char *file, const int lineno, int strict, const char *ifname, unsigned short attr, uint32_t value) { struct ifinfomsg info = { .ifi_family = AF_UNSPEC }; - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; int ret; if (strlen(ifname) >= IFNAMSIZ) { @@ -337,23 +338,23 @@ static int change_ns(const char *file, const int lineno, int strict, return 0; if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } if (!tst_rtnl_add_attr(file, lineno, ctx, attr, &value, sizeof(uint32_t))) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } - ret = tst_rtnl_send_validate(file, lineno, ctx); - tst_rtnl_destroy_context(file, lineno, ctx); + ret = tst_netlink_send_validate(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); if (strict && !ret) { tst_brk_(file, lineno, TBROK, "Failed to move %s to another namespace: %s", ifname, - tst_strerrno(tst_rtnl_errno)); + tst_strerrno(tst_netlink_errno)); } return ret; @@ -377,7 +378,7 @@ static int modify_route(const char *file, const int lineno, int strict, size_t srclen, const void *dstaddr, unsigned int dstprefix, size_t dstlen, const void *gateway, size_t gatewaylen) { - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; int ret; int32_t index; struct rtmsg info = { @@ -420,35 +421,35 @@ static int modify_route(const char *file, const int lineno, int strict, if (srcaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_SRC, srcaddr, srclen)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } if (dstaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_DST, dstaddr, dstlen)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } if (gateway && !tst_rtnl_add_attr(file, lineno, ctx, RTA_GATEWAY, gateway, gatewaylen)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } if (ifname && !tst_rtnl_add_attr(file, lineno, ctx, RTA_OIF, &index, sizeof(index))) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } - ret = tst_rtnl_send_validate(file, lineno, ctx); - tst_rtnl_destroy_context(file, lineno, ctx); + ret = tst_netlink_send_validate(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); if (strict && !ret) { tst_brk_(file, lineno, TBROK, "Failed to modify network route: %s", - tst_strerrno(tst_rtnl_errno)); + tst_strerrno(tst_netlink_errno)); } return ret; @@ -526,9 +527,9 @@ static int modify_qdisc(const char *file, const int lineno, int strict, const char *object, unsigned int action, unsigned int nl_flags, const char *ifname, unsigned int family, unsigned int parent, unsigned int handle, unsigned int info, const char *qd_kind, - const struct tst_rtnl_attr_list *config) + const struct tst_netlink_attr_list *config) { - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; int ret; struct tcmsg msg = { .tcm_family = family, @@ -560,22 +561,22 @@ static int modify_qdisc(const char *file, const int lineno, int strict, return 0; if (!tst_rtnl_add_attr_string(file, lineno, ctx, TCA_KIND, qd_kind)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } if (config && !tst_rtnl_add_attr_list(file, lineno, ctx, config)) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return 0; } - ret = tst_rtnl_send_validate(file, lineno, ctx); - tst_rtnl_destroy_context(file, lineno, ctx); + ret = tst_netlink_send_validate(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); if (strict && !ret) { tst_brk_(file, lineno, TBROK, "Failed to modify %s: %s", object, - tst_strerrno(tst_rtnl_errno)); + tst_strerrno(tst_netlink_errno)); } return ret; @@ -584,7 +585,7 @@ static int modify_qdisc(const char *file, const int lineno, int strict, int tst_netdev_add_qdisc(const char *file, const int lineno, int strict, const char *ifname, unsigned int family, unsigned int parent, unsigned int handle, const char *qd_kind, - const struct tst_rtnl_attr_list *config) + const struct tst_netlink_attr_list *config) { return modify_qdisc(file, lineno, strict, "queueing discipline", RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL, ifname, family, @@ -603,7 +604,7 @@ int tst_netdev_remove_qdisc(const char *file, const int lineno, int strict, int tst_netdev_add_traffic_class(const char *file, const int lineno, int strict, const char *ifname, unsigned int parent, unsigned int handle, const char *qd_kind, - const struct tst_rtnl_attr_list *config) + const struct tst_netlink_attr_list *config) { return modify_qdisc(file, lineno, strict, "traffic class", RTM_NEWTCLASS, NLM_F_CREATE | NLM_F_EXCL, ifname, AF_UNSPEC, @@ -622,7 +623,7 @@ int tst_netdev_remove_traffic_class(const char *file, const int lineno, int tst_netdev_add_traffic_filter(const char *file, const int lineno, int strict, const char *ifname, unsigned int parent, unsigned int handle, unsigned int protocol, unsigned int priority, - const char *f_kind, const struct tst_rtnl_attr_list *config) + const char *f_kind, const struct tst_netlink_attr_list *config) { return modify_qdisc(file, lineno, strict, "traffic filter", RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_EXCL, ifname, AF_UNSPEC, diff --git a/lib/tst_rtnetlink.c b/lib/tst_netlink.c old mode 100755 new mode 100644 similarity index 61% rename from lib/tst_rtnetlink.c rename to lib/tst_netlink.c index a2411dfd..d61cc8b8 --- a/lib/tst_rtnetlink.c +++ b/lib/tst_netlink.c @@ -13,9 +13,9 @@ #include #define TST_NO_DEFAULT_MAIN #include "tst_test.h" -#include "tst_rtnetlink.h" +#include "tst_netlink.h" -struct tst_rtnl_context { +struct tst_netlink_context { int socket; pid_t pid; uint32_t seq; @@ -24,10 +24,10 @@ struct tst_rtnl_context { struct nlmsghdr *curmsg; }; -int tst_rtnl_errno; +int tst_netlink_errno; -static int tst_rtnl_grow_buffer(const char *file, const int lineno, - struct tst_rtnl_context *ctx, size_t size) +static int netlink_grow_buffer(const char *file, const int lineno, + struct tst_netlink_context *ctx, size_t size) { size_t needed, offset, curlen = NLMSG_ALIGN(ctx->datalen); char *buf; @@ -52,21 +52,25 @@ static int tst_rtnl_grow_buffer(const char *file, const int lineno, return 1; } -void tst_rtnl_destroy_context(const char *file, const int lineno, - struct tst_rtnl_context *ctx) +void tst_netlink_destroy_context(const char *file, const int lineno, + struct tst_netlink_context *ctx) { + if (!ctx) + return; + safe_close(file, lineno, NULL, ctx->socket); free(ctx->buffer); free(ctx); } -struct tst_rtnl_context *tst_rtnl_create_context(const char *file, - const int lineno) +struct tst_netlink_context *tst_netlink_create_context(const char *file, + const int lineno, int protocol) { - struct tst_rtnl_context *ctx; + struct tst_netlink_context *ctx; struct sockaddr_nl addr = { .nl_family = AF_NETLINK }; - ctx = safe_malloc(file, lineno, NULL, sizeof(struct tst_rtnl_context)); + ctx = safe_malloc(file, lineno, NULL, + sizeof(struct tst_netlink_context)); if (!ctx) return NULL; @@ -78,7 +82,7 @@ struct tst_rtnl_context *tst_rtnl_create_context(const char *file, ctx->datalen = 0; ctx->curmsg = NULL; ctx->socket = safe_socket(file, lineno, NULL, AF_NETLINK, - SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE); + SOCK_DGRAM | SOCK_CLOEXEC, protocol); if (ctx->socket < 0) { free(ctx); @@ -87,14 +91,14 @@ struct tst_rtnl_context *tst_rtnl_create_context(const char *file, if (safe_bind(file, lineno, NULL, ctx->socket, (struct sockaddr *)&addr, sizeof(addr))) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return NULL; } ctx->buffer = safe_malloc(file, lineno, NULL, ctx->bufsize); if (!ctx->buffer) { - tst_rtnl_destroy_context(file, lineno, ctx); + tst_netlink_destroy_context(file, lineno, ctx); return NULL; } @@ -103,7 +107,7 @@ struct tst_rtnl_context *tst_rtnl_create_context(const char *file, return ctx; } -void tst_rtnl_free_message(struct tst_rtnl_message *msg) +void tst_netlink_free_message(struct tst_netlink_message *msg) { if (!msg) return; @@ -114,8 +118,8 @@ void tst_rtnl_free_message(struct tst_rtnl_message *msg) free(msg); } -int tst_rtnl_send(const char *file, const int lineno, - struct tst_rtnl_context *ctx) +int tst_netlink_send(const char *file, const int lineno, + struct tst_netlink_context *ctx) { int ret; struct sockaddr_nl addr = { .nl_family = AF_NETLINK }; @@ -136,7 +140,7 @@ int tst_rtnl_send(const char *file, const int lineno, if (ctx->curmsg->nlmsg_flags & NLM_F_MULTI) { struct nlmsghdr eom = { .nlmsg_type = NLMSG_DONE }; - if (!tst_rtnl_add_message(file, lineno, ctx, &eom, NULL, 0)) + if (!tst_netlink_add_message(file, lineno, ctx, &eom, NULL, 0)) return 0; /* NLMSG_DONE message must not have NLM_F_MULTI flag */ @@ -153,7 +157,7 @@ int tst_rtnl_send(const char *file, const int lineno, return ret; } -int tst_rtnl_wait(struct tst_rtnl_context *ctx) +int tst_netlink_wait(struct tst_netlink_context *ctx) { struct pollfd fdinfo = { .fd = ctx->socket, @@ -163,11 +167,11 @@ int tst_rtnl_wait(struct tst_rtnl_context *ctx) return poll(&fdinfo, 1, 1000); } -struct tst_rtnl_message *tst_rtnl_recv(const char *file, const int lineno, - struct tst_rtnl_context *ctx) +struct tst_netlink_message *tst_netlink_recv(const char *file, + const int lineno, struct tst_netlink_context *ctx) { char tmp, *tmpbuf, *buffer = NULL; - struct tst_rtnl_message *ret; + struct tst_netlink_message *ret; struct nlmsghdr *ptr; size_t retsize, bufsize = 0; ssize_t size; @@ -215,7 +219,7 @@ struct tst_rtnl_message *tst_rtnl_recv(const char *file, const int lineno, for (; size_left > 0 && NLMSG_OK(ptr, size_left); msgcount++) ptr = NLMSG_NEXT(ptr, size_left); - retsize = (msgcount + 1) * sizeof(struct tst_rtnl_message); + retsize = (msgcount + 1) * sizeof(struct tst_netlink_message); ret = safe_malloc(file, lineno, NULL, retsize); if (!ret) { @@ -239,14 +243,14 @@ struct tst_rtnl_message *tst_rtnl_recv(const char *file, const int lineno, return ret; } -int tst_rtnl_add_message(const char *file, const int lineno, - struct tst_rtnl_context *ctx, const struct nlmsghdr *header, +int tst_netlink_add_message(const char *file, const int lineno, + struct tst_netlink_context *ctx, const struct nlmsghdr *header, const void *payload, size_t payload_size) { size_t size; unsigned int extra_flags = 0; - if (!tst_rtnl_grow_buffer(file, lineno, ctx, NLMSG_SPACE(payload_size))) + if (!netlink_grow_buffer(file, lineno, ctx, NLMSG_SPACE(payload_size))) return 0; if (!ctx->curmsg) { @@ -279,8 +283,88 @@ int tst_rtnl_add_message(const char *file, const int lineno, return 1; } +int tst_netlink_add_attr(const char *file, const int lineno, + struct tst_netlink_context *ctx, unsigned short type, + const void *data, unsigned short len) +{ + size_t size = NLA_HDRLEN + NLA_ALIGN(len); + struct nlattr *attr; + + if (!ctx->curmsg) { + tst_brk_(file, lineno, TBROK, + "%s(): No message to add attributes to", __func__); + return 0; + } + + if (!netlink_grow_buffer(file, lineno, ctx, size)) + return 0; + + size = NLMSG_ALIGN(ctx->curmsg->nlmsg_len); + attr = (struct nlattr *)(((char *)ctx->curmsg) + size); + attr->nla_type = type; + attr->nla_len = NLA_HDRLEN + len; + memcpy(((char *)attr) + NLA_HDRLEN, data, len); + ctx->curmsg->nlmsg_len = size + attr->nla_len; + ctx->datalen = NLMSG_ALIGN(ctx->datalen) + attr->nla_len; + + return 1; +} + +int tst_netlink_add_attr_string(const char *file, const int lineno, + struct tst_netlink_context *ctx, unsigned short type, + const char *data) +{ + return tst_netlink_add_attr(file, lineno, ctx, type, data, + strlen(data) + 1); +} + +int tst_netlink_add_attr_list(const char *file, const int lineno, + struct tst_netlink_context *ctx, + const struct tst_netlink_attr_list *list) +{ + int i, ret; + size_t offset; + + for (i = 0; list[i].len >= 0; i++) { + if (list[i].len > USHRT_MAX) { + tst_brk_(file, lineno, TBROK, + "%s(): Attribute value too long", __func__); + return -1; + } + + offset = NLMSG_ALIGN(ctx->datalen); + ret = tst_netlink_add_attr(file, lineno, ctx, list[i].type, + list[i].data, list[i].len); + + if (!ret) + return -1; + + if (list[i].sublist) { + struct rtattr *attr; + + ret = tst_netlink_add_attr_list(file, lineno, ctx, + list[i].sublist); + + if (ret < 0) + return ret; + + attr = (struct rtattr *)(ctx->buffer + offset); + + if (ctx->datalen - offset > USHRT_MAX) { + tst_brk_(file, lineno, TBROK, + "%s(): Sublist too long", __func__); + return -1; + } + + attr->rta_len = ctx->datalen - offset; + } + } + + return i; +} + int tst_rtnl_add_attr(const char *file, const int lineno, - struct tst_rtnl_context *ctx, unsigned short type, + struct tst_netlink_context *ctx, unsigned short type, const void *data, unsigned short len) { size_t size; @@ -292,7 +376,7 @@ int tst_rtnl_add_attr(const char *file, const int lineno, return 0; } - if (!tst_rtnl_grow_buffer(file, lineno, ctx, RTA_SPACE(len))) + if (!netlink_grow_buffer(file, lineno, ctx, RTA_SPACE(len))) return 0; size = NLMSG_ALIGN(ctx->curmsg->nlmsg_len); @@ -307,7 +391,7 @@ int tst_rtnl_add_attr(const char *file, const int lineno, } int tst_rtnl_add_attr_string(const char *file, const int lineno, - struct tst_rtnl_context *ctx, unsigned short type, + struct tst_netlink_context *ctx, unsigned short type, const char *data) { return tst_rtnl_add_attr(file, lineno, ctx, type, data, @@ -315,8 +399,8 @@ int tst_rtnl_add_attr_string(const char *file, const int lineno, } int tst_rtnl_add_attr_list(const char *file, const int lineno, - struct tst_rtnl_context *ctx, - const struct tst_rtnl_attr_list *list) + struct tst_netlink_context *ctx, + const struct tst_netlink_attr_list *list) { int i, ret; size_t offset; @@ -359,8 +443,8 @@ int tst_rtnl_add_attr_list(const char *file, const int lineno, return i; } -int tst_rtnl_check_acks(const char *file, const int lineno, - struct tst_rtnl_context *ctx, struct tst_rtnl_message *res) +int tst_netlink_check_acks(const char *file, const int lineno, + struct tst_netlink_context *ctx, struct tst_netlink_message *res) { struct nlmsghdr *msg = (struct nlmsghdr *)ctx->buffer; int size_left = ctx->datalen; @@ -371,18 +455,19 @@ int tst_rtnl_check_acks(const char *file, const int lineno, if (!(msg->nlmsg_flags & NLM_F_ACK)) continue; - while (res->header && res->header->nlmsg_seq != msg->nlmsg_seq) + while (res->header && !(res->err && res->err->error) && + res->header->nlmsg_seq != msg->nlmsg_seq) res++; - if (!res->err || res->header->nlmsg_seq != msg->nlmsg_seq) { - tst_brk_(file, lineno, TBROK, - "No ACK found for Netlink message %u", - msg->nlmsg_seq); + if (res->err && res->err->error) { + tst_netlink_errno = -res->err->error; return 0; } - if (res->err->error) { - tst_rtnl_errno = -res->err->error; + if (!res->header || res->header->nlmsg_seq != msg->nlmsg_seq) { + tst_brk_(file, lineno, TBROK, + "No ACK found for Netlink message %u", + msg->nlmsg_seq); return 0; } } @@ -390,25 +475,25 @@ int tst_rtnl_check_acks(const char *file, const int lineno, return 1; } -int tst_rtnl_send_validate(const char *file, const int lineno, - struct tst_rtnl_context *ctx) +int tst_netlink_send_validate(const char *file, const int lineno, + struct tst_netlink_context *ctx) { - struct tst_rtnl_message *response; + struct tst_netlink_message *response; int ret; - tst_rtnl_errno = 0; + tst_netlink_errno = 0; - if (tst_rtnl_send(file, lineno, ctx) <= 0) + if (tst_netlink_send(file, lineno, ctx) <= 0) return 0; - tst_rtnl_wait(ctx); - response = tst_rtnl_recv(file, lineno, ctx); + tst_netlink_wait(ctx); + response = tst_netlink_recv(file, lineno, ctx); if (!response) return 0; - ret = tst_rtnl_check_acks(file, lineno, ctx, response); - tst_rtnl_free_message(response); + ret = tst_netlink_check_acks(file, lineno, ctx, response); + tst_netlink_free_message(response); return ret; } diff --git a/lib/tst_path_exists.c b/lib/tst_path_exists.c new file mode 100644 index 00000000..333c4b0e --- /dev/null +++ b/lib/tst_path_exists.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Linux Test Project, 2011-2021 + * Copyright (c) Cyril Hrubis 2024 + */ + +#include +#include +#include +#include +#include "tst_fs.h" + +int tst_path_exists(const char *fmt, ...) +{ + va_list ap; + char pathbuf[PATH_MAX]; + + va_start(ap, fmt); + vsnprintf(pathbuf, sizeof(pathbuf), fmt, ap); + va_end(ap); + + return access(pathbuf, F_OK) == 0; +} diff --git a/lib/tst_pid.c b/lib/tst_pid.c index cfaa5db3..4e9dc7a5 100755 --- a/lib/tst_pid.c +++ b/lib/tst_pid.c @@ -166,3 +166,8 @@ pid_t tst_getpid(void) { return syscall(SYS_getpid); } + +pid_t tst_gettid(void) +{ + return syscall(SYS_gettid); +} diff --git a/lib/tst_process_state.c b/lib/tst_process_state.c index 08a9d096..033af202 100755 --- a/lib/tst_process_state.c +++ b/lib/tst_process_state.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012-2014 Cyril Hrubis chrubis@suse.cz * Copyright (c) 2021 Xie Ziyao @@ -22,7 +22,7 @@ int tst_process_state_wait(const char *file, const int lineno, for (;;) { safe_file_scanf(file, lineno, cleanup_fn, proc_path, - "%*i %*s %c", &cur_state); + "%*[^)]%*c %c", &cur_state); if (state == cur_state) break; @@ -54,7 +54,7 @@ int tst_process_state_wait2(pid_t pid, const char state) return 1; } - if (fscanf(f, "%*i %*s %c", &cur_state) != 1) { + if (fscanf(f, "%*[^)]%*c %c", &cur_state) != 1) { fclose(f); fprintf(stderr, "Failed to read '%s': %s\n", proc_path, strerror(errno)); diff --git a/lib/tst_res.c b/lib/tst_res.c index e0896eb0..f50c0727 100755 --- a/lib/tst_res.c +++ b/lib/tst_res.c @@ -141,11 +141,14 @@ struct pair { #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; \ +#define PAIR_LOOKUP(pair_arr, idx) do { \ + static char pair_str_buf__[16]; \ + if (idx < 0 || (size_t)idx >= ARRAY_SIZE(pair_arr) || \ + pair_arr[idx].name == NULL) { \ + snprintf(pair_str_buf__, sizeof(pair_str_buf__), "%i", idx); \ + return pair_str_buf__; \ + } \ + return pair_arr[idx].name; \ } while (0) const char *strttype(int ttype) @@ -174,6 +177,11 @@ static void tst_res__(const char *file, const int lineno, int ttype, int len = 0; int ttype_result = TTYPE_RESULT(ttype); + if (ttype_result == TDEBUG) { + printf("%s: %i: TDEBUG is not supported\n", __func__, __LINE__); + abort(); + } + if (file && (ttype_result != TPASS && ttype_result != TINFO)) len = sprintf(tmesg, "%s:%d: ", file, lineno); EXPAND_VAR_ARGS(tmesg + len, arg_fmt, USERMESG - len); @@ -466,14 +474,6 @@ void tst_record_childstatus(void (*cleanup)(void), pid_t child) } } -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. diff --git a/lib/tst_safe_macros.c b/lib/tst_safe_macros.c index c4cdc87e..cdc8c7dd 100755 --- a/lib/tst_safe_macros.c +++ b/lib/tst_safe_macros.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Cyril Hrubis + * Copyright (c) 2017-2024 Linux Test Project */ #define _GNU_SOURCE @@ -9,6 +10,7 @@ #include #include #include +#include #include "config.h" #ifdef HAVE_SYS_FANOTIFY_H # include @@ -225,8 +227,8 @@ int safe_setresuid(const char *file, const int lineno, } int safe_sigaction(const char *file, const int lineno, - int signum, const struct sigaction *act, - struct sigaction *oldact) + int signum, const struct sigaction *act, + struct sigaction *oldact) { int rval; @@ -246,7 +248,7 @@ int safe_sigaction(const char *file, const int lineno, } int safe_sigaddset(const char *file, const int lineno, - sigset_t *sigs, int signo) + sigset_t *sigs, int signo) { int rval; @@ -265,8 +267,7 @@ int safe_sigaddset(const char *file, const int lineno, return rval; } -int safe_sigdelset(const char *file, const int lineno, - sigset_t *sigs, int signo) +int safe_sigdelset(const char *file, const int lineno, sigset_t *sigs, int signo) { int rval; @@ -285,8 +286,7 @@ int safe_sigdelset(const char *file, const int lineno, return rval; } -int safe_sigemptyset(const char *file, const int lineno, - sigset_t *sigs) +int safe_sigemptyset(const char *file, const int lineno, sigset_t *sigs) { int rval; @@ -334,7 +334,7 @@ static const char *strhow(int how) } int safe_sigprocmask(const char *file, const int lineno, - int how, sigset_t *set, sigset_t *oldset) + int how, sigset_t *set, sigset_t *oldset) { int rval; @@ -353,8 +353,7 @@ int safe_sigprocmask(const char *file, const int lineno, return rval; } -int safe_sigwait(const char *file, const int lineno, - sigset_t *set, int *sig) +int safe_sigwait(const char *file, const int lineno, sigset_t *set, int *sig) { int rval; @@ -548,6 +547,20 @@ int safe_dup2(const char *file, const int lineno, int oldfd, int newfd) return rval; } +void *safe_calloc(const char *file, const int lineno, size_t nmemb, size_t size) +{ + void *rval; + + rval = calloc(nmemb, size); + + if (rval == NULL) { + tst_brk_(file, lineno, TBROK | TERRNO, + "calloc(%zu, %zu) failed", nmemb, size); + } + + return rval; +} + void *safe_realloc(const char *file, const int lineno, void *ptr, size_t size) { void *ret; @@ -591,3 +604,228 @@ void safe_cmd(const char *file, const int lineno, const char *const argv[], tst_brk_(file, lineno, TBROK, "%s failed (%d)", argv[0], rval); } } + +int safe_msync(const char *file, const int lineno, void *addr, + size_t length, int flags) +{ + int rval; + + rval = msync(addr, length, flags); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "msync(%p, %zu, %d) failed", addr, length, flags); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid msync(%p, %zu, %d) return value %d", + addr, length, flags, rval); + } + + return rval; +} + +void safe_print_file(const char *file, const int lineno, char *path) +{ + FILE *pfile; + char line[PATH_MAX]; + + tst_res(TINFO, "=== %s ===", path); + + pfile = safe_fopen(file, lineno, NULL, path, "r"); + + while (fgets(line, sizeof(line), pfile)) + fprintf(stderr, "%s", line); + + safe_fclose(file, lineno, NULL, pfile); +} + +int safe_sscanf(const char *file, const int lineno, const char *restrict buffer, const char *restrict format, ...) +{ + va_list args; + + va_start(args, format); + int ret = vsscanf(buffer, format, args); + + va_end(args); + int placeholders = tst_count_scanf_conversions(format); + + if (ret == EOF) + tst_brk_(file, lineno, TBROK | TERRNO, "got EOF from sscanf()"); + + if (ret != placeholders) + tst_brk_(file, lineno, TBROK | TERRNO, "wrong number of conversion, expected %d, got %d", placeholders, ret); + + return ret; +} + +#define PROT_FLAG_STR(f) #f " | " +void tst_prot_to_str(const int prot, char *buf) +{ + char *ptr = buf; + + if (prot == PROT_NONE) { + strcpy(buf, "PROT_NONE"); + return; + } + + if (prot & PROT_READ) { + strcpy(ptr, PROT_FLAG_STR(PROT_READ)); + ptr += sizeof(PROT_FLAG_STR(PROT_READ)) - 1; + } + + if (prot & PROT_WRITE) { + strcpy(ptr, PROT_FLAG_STR(PROT_WRITE)); + ptr += sizeof(PROT_FLAG_STR(PROT_WRITE)) - 1; + } + + if (prot & PROT_EXEC) { + strcpy(ptr, PROT_FLAG_STR(PROT_EXEC)); + ptr += sizeof(PROT_FLAG_STR(PROT_EXEC)) - 1; + } + + if (buf != ptr) + ptr[-3] = 0; +} + +int safe_mprotect(const char *file, const int lineno, + char *addr, size_t len, int prot) +{ + int rval; + char prot_buf[512]; + + tst_prot_to_str(prot, prot_buf); + + tst_res_(file, lineno, TDEBUG, + "mprotect(%p, %zi, %s(%x))", addr, len, prot_buf, prot); + + rval = mprotect(addr, len, prot); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "mprotect(%p, %zi, %s(%x))", addr, len, prot_buf, prot); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "mprotect(%p, %zi, %s(%x)) return value %d", + addr, len, prot_buf, prot, rval); + } + + return rval; +} + +int safe_prctl(const char *file, const int lineno, + int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + int rval; + + rval = prctl(option, arg2, arg3, arg4, arg5); + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "prctl(%d, %lu, %lu, %lu, %lu)", + option, arg2, arg3, arg4, arg5); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid prctl(%d, %lu, %lu, %lu, %lu) return value %d", + option, arg2, arg3, arg4, arg5, rval); + } + + return rval; +} + +ssize_t safe_readv(const char *file, const int lineno, char len_strict, + int fildes, const struct iovec *iov, int iovcnt) +{ + ssize_t rval, nbyte; + int i; + + for (i = 0, nbyte = 0; i < iovcnt; i++) + nbyte += iov[i].iov_len; + + rval = readv(fildes, iov, iovcnt); + + if (rval == -1 || (len_strict && rval != nbyte)) { + tst_brk_(file, lineno, TBROK | TERRNO, + "readv(%d,%p,%d) failed", fildes, iov, iovcnt); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid readv(%d,%p,%d) return value %zd", + fildes, iov, iovcnt, rval); + } + + return rval; +} + +int safe_symlinkat(const char *file, const int lineno, + const char *oldpath, const int newdirfd, const char *newpath) +{ + int rval; + + rval = symlinkat(oldpath, newdirfd, newpath); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "symlinkat(%s,%d,%s) failed", oldpath, newdirfd, newpath); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid symlinkat(%s,%d,%s) return value %d", oldpath, + newdirfd, newpath, rval); + } + + return rval; +} + +ssize_t safe_writev(const char *file, const int lineno, char len_strict, + int fildes, const struct iovec *iov, int iovcnt) +{ + ssize_t rval, nbyte; + int i; + + for (i = 0, nbyte = 0; i < iovcnt; i++) + nbyte += iov[i].iov_len; + + rval = writev(fildes, iov, iovcnt); + + if (rval == -1 || (len_strict && rval != nbyte)) { + tst_brk_(file, lineno, TBROK | TERRNO, + "writev(%d,%p,%d) failed", fildes, iov, iovcnt); + } else if (rval < 0) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid writev(%d,%p,%d) return value %zd", + fildes, iov, iovcnt, rval); + } + + return rval; +} + +char *safe_ptsname(const char *const file, const int lineno, int masterfd) +{ + char *name; + + name = ptsname(masterfd); + + if (!name) { + tst_brk_(file, lineno, TBROK | TERRNO, + "ptsname(%d) failed", masterfd); + } + + return name; +} + +int safe_statvfs(const char *file, const int lineno, + const char *path, struct statvfs *buf) +{ + int rval; + + rval = statvfs(path, buf); + + if (rval == -1) { + tst_brk_(file, lineno, TBROK | TERRNO, + "statvfs(%s,%p) failed", path, buf); + } else if (rval) { + tst_brk_(file, lineno, TBROK | TERRNO, + "Invalid statvfs(%s,%p) return value %d", path, buf, + rval); + } + + return rval; +} diff --git a/lib/tst_lockdown.c b/lib/tst_security.c old mode 100755 new mode 100644 similarity index 65% rename from lib/tst_lockdown.c rename to lib/tst_security.c index 38d83088..c5152713 --- a/lib/tst_lockdown.c +++ b/lib/tst_security.c @@ -1,18 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Linux Test Project, 2020-2024 + */ #define TST_NO_DEFAULT_MAIN +#define PATH_FIPS "/proc/sys/crypto/fips_enabled" #define PATH_LOCKDOWN "/sys/kernel/security/lockdown" - -#include -#include -#include - -#include "tst_test.h" -#include "tst_safe_macros.h" -#include "tst_safe_stdio.h" -#include "tst_lockdown.h" -#include "tst_private.h" +#define SELINUX_PATH "/sys/fs/selinux" +#define SELINUX_STATUS_PATH (SELINUX_PATH "/enforce") #if defined(__powerpc64__) || defined(__ppc64__) # define SECUREBOOT_VAR "/proc/device-tree/ibm,secure-boot" @@ -22,30 +18,54 @@ # define VAR_DATA_SIZE 5 #endif -int tst_secureboot_enabled(void) +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_macros.h" +#include "tst_safe_stdio.h" +#include "tst_security.h" +#include "tst_private.h" + +int tst_lsm_enabled(const char *name) { int fd; - char data[5]; + char *ptr; + char data[BUFSIZ]; - if (access(SECUREBOOT_VAR, F_OK)) { - tst_res(TINFO, "SecureBoot sysfs file not available"); - return -1; - } + if (access(LSM_SYS_FILE, F_OK)) + tst_brk(TCONF, "%s file is not present", LSM_SYS_FILE); - fd = open(SECUREBOOT_VAR, O_RDONLY); - - if (fd == -1) { - tst_res(TINFO | TERRNO, - "Cannot open SecureBoot file"); - return -1; - } else if (fd < 0) { - tst_brk(TBROK | TERRNO, "Invalid open() return value %d", fd); - return -1; - } - SAFE_READ(1, fd, data, VAR_DATA_SIZE); + fd = SAFE_OPEN(LSM_SYS_FILE, O_RDONLY); + SAFE_READ(0, fd, data, BUFSIZ); SAFE_CLOSE(fd); - tst_res(TINFO, "SecureBoot: %s", data[VAR_DATA_SIZE - 1] ? "on" : "off"); - return data[VAR_DATA_SIZE - 1]; + + ptr = strtok(data, ","); + while (ptr != NULL) { + if (!strcmp(ptr, name)) { + tst_res(TINFO, "%s is enabled", name); + return 1; + } + + ptr = strtok(NULL, ","); + } + + return 0; +} + +int tst_fips_enabled(void) +{ + int fips = 0; + + if (access(PATH_FIPS, R_OK) == 0) + SAFE_FILE_SCANF(PATH_FIPS, "%d", &fips); + + tst_res(TINFO, "FIPS: %s", fips ? "on" : "off"); + + return fips; } int tst_lockdown_enabled(void) @@ -83,3 +103,41 @@ int tst_lockdown_enabled(void) return ret; } + +int tst_secureboot_enabled(void) +{ + int fd; + char data[5]; + + if (access(SECUREBOOT_VAR, F_OK)) { + tst_res(TINFO, "SecureBoot sysfs file not available"); + return -1; + } + + fd = open(SECUREBOOT_VAR, O_RDONLY); + + if (fd == -1) { + tst_res(TINFO | TERRNO, + "Cannot open SecureBoot file"); + return -1; + } else if (fd < 0) { + tst_brk(TBROK | TERRNO, "Invalid open() return value %d", fd); + return -1; + } + SAFE_READ(1, fd, data, VAR_DATA_SIZE); + SAFE_CLOSE(fd); + tst_res(TINFO, "SecureBoot: %s", data[VAR_DATA_SIZE - 1] ? "on" : "off"); + return data[VAR_DATA_SIZE - 1]; +} + +int tst_selinux_enforcing(void) +{ + int res = 0; + + if (access(SELINUX_STATUS_PATH, F_OK) == 0) + SAFE_FILE_SCANF(SELINUX_STATUS_PATH, "%d", &res); + + tst_res(TINFO, "SELinux enforcing: %s", res ? "on" : "off"); + + return res; +} diff --git a/lib/tst_supported_fs_types.c b/lib/tst_supported_fs_types.c index d4911fa3..bbd5a5fb 100755 --- a/lib/tst_supported_fs_types.c +++ b/lib/tst_supported_fs_types.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Cyril Hrubis + * Copyright (c) Linux Test Project, 2018-2023 */ #include @@ -13,6 +14,7 @@ #define TST_NO_DEFAULT_MAIN #include "tst_test.h" #include "tst_fs.h" +#include "tst_private.h" /* * NOTE: new filesystem should be also added to @@ -24,6 +26,7 @@ static const char *const fs_type_whitelist[] = { "ext4", "xfs", "btrfs", + "bcachefs", "vfat", "exfat", "ntfs", @@ -31,6 +34,11 @@ static const char *const fs_type_whitelist[] = { NULL }; +static const char *const fs_type_fuse_blacklist[] = { + "bcachefs", + NULL, +}; + static const char *fs_types[ARRAY_SIZE(fs_type_whitelist)]; static int has_mkfs(const char *fs_type) @@ -94,6 +102,11 @@ static enum tst_fs_impl has_kernel_support(const char *fs_type) SAFE_RMDIR(template); + if (tst_fs_in_skiplist(fs_type, fs_type_fuse_blacklist)) { + tst_res(TINFO, "Skipping %s because of FUSE blacklist", fs_type); + return TST_FS_UNSUPPORTED; + } + /* Is FUSE supported by kernel? */ if (fuse_supported == -1) { ret = open("/dev/fuse", O_RDWR); @@ -135,40 +148,61 @@ enum tst_fs_impl tst_fs_is_supported(const char *fs_type) return TST_FS_UNSUPPORTED; } +static int fs_could_be_used(const char *fs_type, const char *const *skiplist, + int skip_fuse) +{ + enum tst_fs_impl sup; + + if (tst_fs_in_skiplist(fs_type, skiplist)) { + tst_res(TINFO, "Skipping %s as requested by the test", + fs_type); + return 0; + } + + sup = tst_fs_is_supported(fs_type); + + if (skip_fuse && sup == TST_FS_FUSE) { + tst_res(TINFO, + "Skipping FUSE based %s as requested by the test", + fs_type); + return 0; + } + + return sup != TST_FS_UNSUPPORTED; +} + const char **tst_get_supported_fs_types(const char *const *skiplist) { unsigned int i, j = 0; int skip_fuse; - enum tst_fs_impl sup; - const char *only_fs; + const char *only_fs, *force_only_fs; + + only_fs = getenv("LTP_SINGLE_FS_TYPE"); + force_only_fs = getenv("LTP_FORCE_SINGLE_FS_TYPE"); + + if (only_fs && force_only_fs) { + tst_brk(TBROK, + "Only one of LTP_SINGLE_FS_TYPE and LTP_FORCE_SINGLE_FS_TYPE can be set"); + return NULL; + } skip_fuse = tst_fs_in_skiplist("fuse", skiplist); - only_fs = getenv("LTP_SINGLE_FS_TYPE"); if (only_fs) { tst_res(TINFO, "WARNING: testing only %s", only_fs); - if (tst_fs_is_supported(only_fs)) + if (fs_could_be_used(only_fs, skiplist, skip_fuse)) fs_types[0] = only_fs; return fs_types; } + if (force_only_fs) { + tst_res(TINFO, "WARNING: force testing only %s", force_only_fs); + fs_types[0] = force_only_fs; + return fs_types; + } + for (i = 0; fs_type_whitelist[i]; i++) { - if (tst_fs_in_skiplist(fs_type_whitelist[i], skiplist)) { - tst_res(TINFO, "Skipping %s as requested by the test", - fs_type_whitelist[i]); - continue; - } - - sup = tst_fs_is_supported(fs_type_whitelist[i]); - - if (skip_fuse && sup == TST_FS_FUSE) { - tst_res(TINFO, - "Skipping FUSE based %s as requested by the test", - fs_type_whitelist[i]); - continue; - } - - if (sup) + if (fs_could_be_used(fs_type_whitelist[i], skiplist, skip_fuse)) fs_types[j++] = fs_type_whitelist[i]; } diff --git a/lib/tst_sys_conf.c b/lib/tst_sys_conf.c index c0981dcb..80cd8356 100755 --- a/lib/tst_sys_conf.c +++ b/lib/tst_sys_conf.c @@ -7,6 +7,7 @@ #include #include #include +#include #define TST_NO_DEFAULT_MAIN #include "tst_test.h" @@ -145,3 +146,66 @@ void tst_sys_conf_restore(int verbose) } } +int tst_read_bool_sys_param(const char *filename) +{ + char buf[PATH_MAX]; + int i, fd, ret; + + fd = open(filename, O_RDONLY); + + if (fd < 0) + return -1; + + ret = read(fd, buf, PATH_MAX - 1); + SAFE_CLOSE(fd); + + if (ret < 1) + return -1; + + buf[ret] = '\0'; + + for (i = 0; buf[i] && !isspace(buf[i]); i++) + ; + + buf[i] = '\0'; + + if (isdigit(buf[0])) { + tst_parse_int(buf, &ret, INT_MIN, INT_MAX); + return ret; + } + + if (!strcasecmp(buf, "N")) + return 0; + + /* Assume that any other value than 0 or N means the param is enabled */ + return 1; +} + +long tst_sys_conf_long_get_(const char *file, const int lineno, + const char *path) +{ + long ret; + + safe_file_scanf(file, lineno, NULL, path, "%ld", &ret); + + return ret; +} + +void tst_sys_conf_long_set_(const char *file, const int lineno, + const char *path, long val, int check) +{ + tst_res_(file, lineno, TINFO, "Setting %s to %ld", path, val); + + safe_file_printf(file, lineno, NULL, path, "%ld", val); + + if (check) { + long read_val; + + safe_file_scanf(file, lineno, NULL, path, "%ld", &read_val); + + if (val != read_val) + tst_brk_(file, lineno, TBROK, + "Wrote %ld to %s but read back %ld", + val, path, read_val); + } +} diff --git a/lib/tst_taint.c b/lib/tst_taint.c index 58b96f6e..94459523 100755 --- a/lib/tst_taint.c +++ b/lib/tst_taint.c @@ -33,6 +33,7 @@ static const char *const taint_strings[] = { "K (Live patched)", "X (Auxilary)", "T (Built with struct randomization)", + "N (In-kernel test has been run)", }; static unsigned int tst_taint_read(void) @@ -50,49 +51,17 @@ static int tst_taint_check_kver(unsigned int mask) int r2; int r3 = 0; - if (mask & TST_TAINT_X) { + if (mask & TST_TAINT_N) { + r1 = 6; + r2 = 0; + } else 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 1; } return tst_kvercmp(r1, r2, r3); diff --git a/lib/tst_test.c b/lib/tst_test.c index 0dec00be..53b53af1 100755 --- a/lib/tst_test.c +++ b/lib/tst_test.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2015-2016 Cyril Hrubis - * Copyright (c) Linux Test Project, 2016-2021 + * Copyright (c) 2015-2025 Cyril Hrubis + * Copyright (c) Linux Test Project, 2016-2025 */ +#define _GNU_SOURCE + #include #include #include @@ -13,6 +15,7 @@ #include #include #include +#include #include #include @@ -35,13 +38,14 @@ #include "old_device.h" #include "old_tmpdir.h" #include "ltp-version.h" +#include "tst_hugepage.h" /* * Hack to get TCID defined in newlib tests */ const char *TCID __attribute__((weak)); -/* update also docparse/testinfo.pl */ +/* update also doc/conf.py */ #define LINUX_GIT_URL "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=" #define LINUX_STABLE_GIT_URL "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=" #define GLIBC_GIT_URL "https://sourceware.org/git/?p=glibc.git;a=commit;h=" @@ -50,36 +54,62 @@ const char *TCID __attribute__((weak)); #define DEFAULT_TIMEOUT 30 +/* Magic number is "LTPM" */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define LTP_MAGIC 0x4C54504D +#else +# define LTP_MAGIC 0x4D50544C +#endif + struct tst_test *tst_test; -static const char *tid; +static const char *tcid; 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 */ +static int reproducible_output; +static int quiet_output; -struct results { - int passed; - int skipped; - int failed; - int warnings; - int broken; - unsigned int timeout; - int max_runtime; +struct context { + int32_t lib_pid; + int32_t main_pid; + struct timespec start_time; + int32_t runtime; + int32_t overall_time; + /* + * This is set by a call to tst_brk() with TBROK parameter and means + * that the test should exit immediately. + */ + tst_atomic_t abort_flag; + uint32_t mntpoint_mounted:1; + uint32_t ovl_mounted:1; + uint32_t tdebug:1; }; +struct results { + tst_atomic_t passed; + tst_atomic_t skipped; + tst_atomic_t failed; + tst_atomic_t warnings; + tst_atomic_t broken; +}; + +struct ipc_region { + int32_t magic; + struct context context; + struct results results; + futex_t futexes[]; +}; + +static struct ipc_region *ipc; +static struct context *context; static struct results *results; -static int ipc_fd; - -extern void *tst_futexes; +extern volatile void *tst_futexes; extern unsigned int tst_max_futexes; +static int ipc_fd; static char ipc_path[1064]; -const char *tst_ipc_path = ipc_path; static char shm_path[1024]; @@ -96,7 +126,7 @@ static void setup_ipc(void) if (access("/dev/shm", F_OK) == 0) { snprintf(shm_path, sizeof(shm_path), "/dev/shm/ltp_%s_%d", - tid, getpid()); + tcid, getpid()); } else { char *tmpdir; @@ -105,7 +135,7 @@ static void setup_ipc(void) tmpdir = tst_get_tmpdir(); snprintf(shm_path, sizeof(shm_path), "%s/ltp_%s_%d", - tmpdir, tid, getpid()); + tmpdir, tcid, getpid()); free(tmpdir); } @@ -116,21 +146,28 @@ static void setup_ipc(void) 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); - } + ipc = SAFE_MMAP(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ipc_fd, 0); SAFE_CLOSE(ipc_fd); + memset(ipc, 0, size); + + ipc->magic = LTP_MAGIC; + context = &ipc->context; + results = &ipc->results; + context->lib_pid = getpid(); + if (tst_test->needs_checkpoints) { - tst_futexes = (char *)results + sizeof(struct results); - tst_max_futexes = (size - sizeof(struct results))/sizeof(futex_t); + tst_futexes = ipc->futexes; + tst_max_futexes = (size - offsetof(struct ipc_region, futexes)) / sizeof(futex_t); + } + + /* Set environment variable for exec()'d children */ + if (tst_test->needs_checkpoints || tst_test->child_needs_reinit) { + snprintf(ipc_path, sizeof(ipc_path), IPC_ENV_VAR "=%s", shm_path); + putenv(ipc_path); + } else { + SAFE_UNLINK(shm_path); } } @@ -144,9 +181,11 @@ static void cleanup_ipc(void) 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); + if (ipc) { + msync((void *)ipc, size, MS_SYNC); + munmap((void *)ipc, size); + ipc = NULL; + context = NULL; results = NULL; } } @@ -164,12 +203,65 @@ void tst_reinit(void) 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); - + ipc = SAFE_MMAP(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); SAFE_CLOSE(fd); + + if (ipc->magic != LTP_MAGIC) + tst_brk(TBROK, "Invalid shared memory region (bad magic)"); + + /* Restore the parent context from IPC region */ + context = &ipc->context; + results = &ipc->results; + + tst_futexes = ipc->futexes; + tst_max_futexes = (size - offsetof(struct ipc_region, futexes)) / sizeof(futex_t); + + if (context->tdebug) + tst_res(TINFO, "Restored metadata for PID %d", getpid()); +} + +extern char **environ; + +static unsigned int params_array_len(char *const array[]) +{ + unsigned int ret = 0; + + if (!array) + return 0; + + while (*(array++)) + ret++; + + return ret; +} + +int tst_run_script(const char *script_name, char *const params[]) +{ + int pid; + unsigned int i, params_len = params_array_len(params); + char *argv[params_len + 2]; + + if (!tst_test->runs_script) + tst_brk(TBROK, "runs_script flag must be set!"); + + argv[0] = (char *)script_name; + + if (params) { + for (i = 0; i < params_len; i++) + argv[i+1] = params[i]; + } + + argv[params_len+1] = NULL; + + pid = SAFE_FORK(); + if (pid) + return pid; + + execvpe(script_name, argv, environ); + + tst_brk(TBROK | TERRNO, "execvpe(%s, ...) failed!", script_name); + + return -1; } static void update_results(int ttype) @@ -216,14 +308,23 @@ static void print_result(const char *file, const int lineno, int ttype, res = "TBROK"; break; case TCONF: + if (quiet_output) + return; res = "TCONF"; break; case TWARN: res = "TWARN"; break; case TINFO: + if (quiet_output) + return; res = "TINFO"; break; + case TDEBUG: + if (quiet_output) + return; + res = "TDEBUG"; + break; default: tst_brk(TBROK, "Invalid ttype value %i", ttype); abort(); @@ -256,6 +357,9 @@ static void print_result(const char *file, const int lineno, int ttype, str += ret; size -= ret; + if (reproducible_output) + goto print; + ssize = size - 2; ret = vsnprintf(str, size, fmt, va); str += MIN(ret, ssize); @@ -273,6 +377,7 @@ static void print_result(const char *file, const int lineno, int ttype, "Next message is too long and truncated:"); } +print: snprintf(str, size, "\n"); /* we might be called from signal handler, so use write() */ @@ -330,6 +435,14 @@ void tst_vbrk_(const char *file, const int lineno, int ttype, const char *fmt, va_list va) { print_result(file, lineno, ttype, fmt, va); + + /* + * If tst_brk() is called from some of the C helpers even before the + * library was initialized, just exit. + */ + if (!results || !context->lib_pid) + exit(TTYPE_RESULT(ttype)); + update_results(TTYPE_RESULT(ttype)); /* @@ -338,13 +451,37 @@ void tst_vbrk_(const char *file, const int lineno, int ttype, const char *fmt, * specified but CLONE_THREAD is not. Use direct syscall to avoid * cleanup running in the child. */ - if (tst_getpid() == main_pid) + if (tst_getpid() == context->main_pid) do_test_cleanup(); - if (getpid() == lib_pid) + /* + * The test library process reports result statistics and exits. + */ + if (getpid() == context->lib_pid) do_exit(TTYPE_RESULT(ttype)); - exit(TTYPE_RESULT(ttype)); + /* + * If we get here we are in a child process, either the main child + * running the test or its children. If any of them called tst_brk() + * with TBROK we need to exit the test. Otherwise we just exit the + * current process. + */ + if (TTYPE_RESULT(ttype) == TBROK) { + if (results) + tst_atomic_inc(&context->abort_flag); + + /* + * If TBROK was called from one of the child processes we kill + * the main test process. That in turn triggers the code that + * kills leftover children once the main test process did exit. + */ + if (context->main_pid && tst_getpid() != context->main_pid) { + tst_res(TINFO, "Child process reported TBROK killing the test"); + kill(context->main_pid, SIGKILL); + } + } + + exit(0); } void tst_res_(const char *file, const int lineno, int ttype, @@ -352,6 +489,23 @@ void tst_res_(const char *file, const int lineno, int ttype, { va_list va; + /* + * Suppress TDEBUG output in these cases: + * 1. No context available (e.g., called before IPC initialization) + * 2. Called from the library process, unless explicitly enabled + * 3. Debug output is not enabled (context->tdebug == 0) + */ + if (ttype == TDEBUG) { + if (!context) + return; + + if (context->lib_pid == getpid()) + return; + + if (!context->tdebug) + return; + } + va_start(va, fmt); tst_vres_(file, lineno, ttype, fmt, va); va_end(va); @@ -378,8 +532,6 @@ void tst_printf(const char *const fmt, ...) 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))); @@ -388,15 +540,8 @@ static void check_child_status(pid_t pid, int status) if (!(WIFEXITED(status))) tst_brk(TBROK, "Child (%i) exited abnormally", pid); - ret = WEXITSTATUS(status); - switch (ret) { - case TPASS: - case TBROK: - case TCONF: - break; - default: - tst_brk(TBROK, "Invalid child (%i) exit value %i", pid, ret); - } + if (WEXITSTATUS(status)) + tst_brk(TBROK, "Invalid child (%i) exit value %i", pid, WEXITSTATUS(status)); } void tst_reap_children(void) @@ -492,16 +637,17 @@ static void parse_mul(float *mul, const char *env_name, float min, float max) } } -static int multiply_runtime(int max_runtime) +static int multiply_runtime(unsigned int runtime) { static float runtime_mul = -1; + int min_runtime = 1; - if (max_runtime <= 0) - return max_runtime; + if (tst_test->min_runtime > 0) + min_runtime = tst_test->min_runtime; parse_mul(&runtime_mul, "LTP_RUNTIME_MUL", 0.0099, 100); - return max_runtime * runtime_mul; + return MAX(runtime * runtime_mul, min_runtime); } static struct option { @@ -511,8 +657,8 @@ static struct option { {"h", "-h Prints this help"}, {"i:", "-i n Execute test n times"}, {"I:", "-I x Execute test for n seconds"}, + {"D", "-D Prints debug information"}, {"V", "-V Prints LTP version"}, - {"C:", "-C ARG Run child process with ARG arguments (used internally)"}, }; static void print_help(void) @@ -520,40 +666,44 @@ static void print_help(void) unsigned int i; int timeout, runtime; - /* see doc/user-guide.txt, which lists also shell API variables */ + /* see doc/users/setup_tests.rst, which lists also shell API variables */ fprintf(stderr, "Environment Variables\n"); fprintf(stderr, "---------------------\n"); - fprintf(stderr, "KCONFIG_PATH Specify kernel config file\n"); - fprintf(stderr, "KCONFIG_SKIP_CHECK Skip kernel config check if variable set (not set by default)\n"); - fprintf(stderr, "LTPROOT Prefix for installed LTP (default: /opt/ltp)\n"); - fprintf(stderr, "LTP_COLORIZE_OUTPUT Force colorized output behaviour (y/1 always, n/0: never)\n"); - fprintf(stderr, "LTP_DEV Path to the block device to be used (for .needs_device)\n"); - fprintf(stderr, "LTP_DEV_FS_TYPE Filesystem used for testing (default: %s)\n", DEFAULT_FS_TYPE); - fprintf(stderr, "LTP_SINGLE_FS_TYPE Testing only - specifies filesystem instead all supported (for .all_filesystems)\n"); - fprintf(stderr, "LTP_TIMEOUT_MUL Timeout multiplier (must be a number >=1)\n"); - fprintf(stderr, "LTP_RUNTIME_MUL Runtime multiplier (must be a number >=1)\n"); - fprintf(stderr, "LTP_VIRT_OVERRIDE Overrides virtual machine detection (values: \"\"|kvm|microsoft|xen|zvm)\n"); - fprintf(stderr, "TMPDIR Base directory for template directory (for .needs_tmpdir, default: %s)\n", TEMPDIR); + fprintf(stderr, "KCONFIG_PATH Specify kernel config file\n"); + fprintf(stderr, "KCONFIG_SKIP_CHECK Skip kernel config check if variable set (not set by default)\n"); + fprintf(stderr, "LTPROOT Prefix for installed LTP (default: /opt/ltp)\n"); + fprintf(stderr, "LTP_COLORIZE_OUTPUT Force colorized output behaviour (y/1 always, n/0: never)\n"); + fprintf(stderr, "LTP_DEV Path to the block device to be used (for .needs_device)\n"); + fprintf(stderr, "LTP_DEV_FS_TYPE Filesystem used for testing (default: %s)\n", DEFAULT_FS_TYPE); + fprintf(stderr, "LTP_ENABLE_DEBUG Print debug messages (set 1 or y)\n"); + fprintf(stderr, "LTP_REPRODUCIBLE_OUTPUT Values 1 or y discard the actual content of the messages printed by the test\n"); + fprintf(stderr, "LTP_QUIET Values 1 or y will suppress printing TCONF, TWARN, TINFO, and TDEBUG messages\n"); + fprintf(stderr, "LTP_SINGLE_FS_TYPE Specifies filesystem instead all supported (for .all_filesystems)\n"); + fprintf(stderr, "LTP_FORCE_SINGLE_FS_TYPE Testing only. The same as LTP_SINGLE_FS_TYPE but ignores test skiplist.\n"); + fprintf(stderr, "LTP_TIMEOUT_MUL Timeout multiplier (must be a number >=1)\n"); + fprintf(stderr, "LTP_RUNTIME_MUL Runtime multiplier (must be a number >0)\n"); + fprintf(stderr, "LTP_VIRT_OVERRIDE Overrides virtual machine detection (values: \"\"|kvm|microsoft|xen|zvm)\n"); + fprintf(stderr, "TMPDIR Base directory for template directory (for .needs_tmpdir, default: %s)\n", TEMPDIR); fprintf(stderr, "\n"); fprintf(stderr, "Timeout and runtime\n"); fprintf(stderr, "-------------------\n"); - if (tst_test->max_runtime) { - runtime = multiply_runtime(tst_test->max_runtime); + if (tst_test->timeout == TST_UNLIMITED_TIMEOUT) { + fprintf(stderr, "Test timeout is not limited\n"); + } else { + timeout = tst_multiply_timeout(DEFAULT_TIMEOUT + tst_test->timeout); - if (runtime == TST_UNLIMITED_RUNTIME) { - fprintf(stderr, "Test iteration runtime is not limited\n"); - } else { - fprintf(stderr, "Test iteration runtime cap %ih %im %is\n", - runtime/3600, (runtime%3600)/60, runtime % 60); - } + fprintf(stderr, "Test timeout (not including runtime) %ih %im %is\n", + timeout/3600, (timeout%3600)/60, timeout % 60); } - timeout = tst_multiply_timeout(DEFAULT_TIMEOUT); + if (tst_test->runtime) { + runtime = multiply_runtime(tst_test->runtime); - fprintf(stderr, "Test timeout (not including runtime) %ih %im %is\n", - timeout/3600, (timeout%3600)/60, timeout % 60); + fprintf(stderr, "Test iteration runtime cap %ih %im %is\n", + runtime/3600, (runtime%3600)/60, runtime % 60); + } fprintf(stderr, "\n"); @@ -652,11 +802,6 @@ static void parse_topt(unsigned int topts_len, int opt, char *optarg) *(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(); @@ -679,6 +824,10 @@ static void parse_opts(int argc, char *argv[]) print_help(); tst_brk(TBROK, "Invalid option"); break; + case 'D': + tst_res(TINFO, "Enabling debug info"); + context->tdebug = 1; + break; case 'h': print_help(); print_test_tags(); @@ -687,8 +836,8 @@ static void parse_opts(int argc, char *argv[]) iterations = SAFE_STRTOL(optarg, 0, INT_MAX); break; case 'I': - if (tst_test->max_runtime > 0) - tst_test->max_runtime = SAFE_STRTOL(optarg, 1, INT_MAX); + if (tst_test->runtime > 0) + tst_test->runtime = SAFE_STRTOL(optarg, 1, INT_MAX); else duration = SAFE_STRTOF(optarg, 0.1, HUGE_VALF); break; @@ -696,11 +845,6 @@ static void parse_opts(int argc, char *argv[]) fprintf(stderr, "LTP version: " LTP_VERSION "\n"); exit(0); break; - case 'C': -#ifdef UCLINUX - child_args = optarg; -#endif - break; default: parse_topt(topts_len, opt, optarg); } @@ -851,7 +995,9 @@ static void print_failure_hint(const char *tag, const char *hint, } } -/* update also docparse/testinfo.pl */ +static int show_failure_hints; + +/* update also doc/conf.py */ static void print_failure_hints(void) { print_failure_hint("linux-git", "missing kernel fixes", LINUX_GIT_URL); @@ -861,8 +1007,18 @@ static void print_failure_hints(void) print_failure_hint("musl-git", "missing musl fixes", MUSL_GIT_URL); print_failure_hint("CVE", "vulnerable to CVE(s)", CVE_DB_URL); print_failure_hint("known-fail", "hit by known kernel failures", NULL); + + show_failure_hints = 0; } +/* + * Prints results, cleans up after the test library and exits the test library + * process. The ret parameter is used to pass the result flags in a case of a + * failure before we managed to set up the shared memory where we store the + * results. This allows us to use SAFE_MACROS() in the initialization of the + * shared memory. The ret parameter is not used (passed 0) when called + * explicitly from the rest of the library. + */ static void do_exit(int ret) { if (results) { @@ -871,7 +1027,8 @@ static void do_exit(int ret) if (results->failed) { ret |= TFAIL; - print_failure_hints(); + if (show_failure_hints) + print_failure_hints(); } if (results->skipped && !results->passed) @@ -882,7 +1039,8 @@ static void do_exit(int ret) if (results->broken) { ret |= TBROK; - print_failure_hints(); + if (show_failure_hints) + print_failure_hints(); } fprintf(stderr, "\nSummary:\n"); @@ -898,20 +1056,29 @@ static void do_exit(int ret) exit(ret); } -void check_kver(void) +int check_kver(const char *min_kver, const int brk_nosupp) { + char *msg; int v1, v2, v3; - if (tst_parse_kver(tst_test->min_kver, &v1, &v2, &v3)) { + if (tst_parse_kver(min_kver, &v1, &v2, &v3)) { tst_res(TWARN, "Invalid kernel version %s, expected %%d.%%d.%%d", - tst_test->min_kver); + min_kver); } if (tst_kvercmp(v1, v2, v3) < 0) { - tst_brk(TCONF, "The test requires kernel %s or newer", - tst_test->min_kver); + msg = "The test requires kernel %s or newer"; + + if (brk_nosupp) + tst_brk(TCONF, msg, min_kver); + else + tst_res(TCONF, msg, min_kver); + + return 1; } + + return 0; } static int results_equal(struct results *a, struct results *b) @@ -948,7 +1115,7 @@ static void copy_resources(void) TST_RESOURCE_COPY(NULL, tst_test->resource_files[i], NULL); } -static const char *get_tid(char *argv[]) +static const char *get_tcid(char *argv[]) { char *p; @@ -1004,7 +1171,7 @@ static int prepare_and_mount_ro_fs(const char *dev, const char *mntpoint, return 1; } - mntpoint_mounted = 1; + context->mntpoint_mounted = 1; snprintf(buf, sizeof(buf), "%s/dir/", mntpoint); SAFE_MKDIR(buf, 0777); @@ -1028,17 +1195,20 @@ static void prepare_and_mount_dev_fs(const char *mntpoint) 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; + context->mntpoint_mounted = 1; } } static void prepare_and_mount_hugetlb_fs(void) { + if (access(PATH_HUGEPAGES, F_OK)) + tst_brk(TCONF, "hugetlbfs is not supported"); + SAFE_MOUNT("none", tst_test->mntpoint, "hugetlbfs", 0, NULL); - mntpoint_mounted = 1; + context->mntpoint_mounted = 1; } -int tst_creat_unlinked(const char *path, int flags) +int tst_creat_unlinked(const char *path, int flags, mode_t mode) { char template[PATH_MAX]; int len, c, range; @@ -1046,7 +1216,7 @@ int tst_creat_unlinked(const char *path, int flags) int start[3] = {'0', 'a', 'A'}; snprintf(template, PATH_MAX, "%s/ltp_%.3sXXXXXX", - path, tid); + path, tcid); len = strlen(template) - 1; while (template[len] == 'X') { @@ -1057,7 +1227,7 @@ int tst_creat_unlinked(const char *path, int flags) } flags |= O_CREAT|O_EXCL|O_RDWR; - fd = SAFE_OPEN(template, flags); + fd = SAFE_OPEN(template, flags, mode); SAFE_UNLINK(template); return fd; } @@ -1096,15 +1266,18 @@ static const char *get_device_name(const char *fs_type) return tdev.dev; } -static void prepare_device(void) +static void prepare_device(struct tst_fs *fs) { const char *mnt_data; char buf[1024]; + struct tst_fs dummy = {}; - if (tst_test->format_device) { - SAFE_MKFS(tdev.dev, tdev.fs_type, tst_test->dev_fs_opts, - tst_test->dev_extra_opts); - } + fs = fs ?: &dummy; + + const char *const extra[] = {fs->mkfs_size_opt, NULL}; + + if (tst_test->format_device) + SAFE_MKFS(tdev.dev, tdev.fs_type, fs->mkfs_opts, extra); if (tst_test->needs_rofs) { prepare_and_mount_ro_fs(tdev.dev, tst_test->mntpoint, @@ -1113,12 +1286,12 @@ static void prepare_device(void) } if (tst_test->mount_device) { - mnt_data = limit_tmpfs_mount_size(tst_test->mnt_data, + mnt_data = limit_tmpfs_mount_size(fs->mnt_data, buf, sizeof(buf), tdev.fs_type); - SAFE_MOUNT(get_device_name(tdev.fs_type), tst_test->mntpoint, - tdev.fs_type, tst_test->mnt_flags, mnt_data); - mntpoint_mounted = 1; + SAFE_MOUNT2(get_device_name(tdev.fs_type), tst_test->mntpoint, + tdev.fs_type, fs->mnt_flags, mnt_data, &tdev.is_fuse); + context->mntpoint_mounted = 1; } } @@ -1126,6 +1299,7 @@ static void do_cgroup_requires(void) { const struct tst_cg_opts cg_opts = { .needs_ver = tst_test->needs_cgroup_ver, + .needs_nsdelegate = tst_test->needs_cgroup_nsdelegate, }; const char *const *ctrl_names = tst_test->needs_cgroup_ctrls; @@ -1135,28 +1309,114 @@ static void do_cgroup_requires(void) tst_cg_init(); } +#define tst_set_ulimit(conf) \ + set_ulimit_(__FILE__, __LINE__, (conf)) + +/* + * Set resource limits. + */ +static void set_ulimit_(const char *file, const int lineno, const struct tst_ulimit_val *conf) +{ + struct rlimit rlim; + + safe_getrlimit(file, lineno, conf->resource, &rlim); + + rlim.rlim_cur = conf->rlim_cur; + + if (conf->rlim_cur > rlim.rlim_max) + rlim.rlim_max = conf->rlim_cur; + + tst_res_(file, lineno, TINFO, "Set ulimit resource: %d rlim_cur: %llu rlim_max: %llu", + conf->resource, (long long unsigned int)rlim.rlim_cur, + (long long unsigned int)rlim.rlim_max); + + safe_setrlimit(file, lineno, conf->resource, &rlim); +} + +static unsigned int count_fs_descs(void) +{ + unsigned int ret = 0; + + if (!tst_test->filesystems) + return 0; + + /* + * First entry is special, if it has zero type it's the default entry + * and is either followed by a terminating entry or by filesystem + * description(s) plus terminating entry. + */ + if (!tst_test->filesystems[0].type) + ret = 1; + + while (tst_test->filesystems[ret].type) + ret++; + + return ret; +} + +static const char *default_fs_type(void) +{ + if (!tst_test->filesystems) + return tst_dev_fs_type(); + + if (tst_test->filesystems[0].type) + return tst_test->filesystems[0].type; + + return tst_dev_fs_type(); +} + static void do_setup(int argc, char *argv[]) { + char *tdebug_env = getenv("LTP_ENABLE_DEBUG"); + char *reproducible_env = getenv("LTP_REPRODUCIBLE_OUTPUT"); + char *quiet_env = getenv("LTP_QUIET"); + if (!tst_test) tst_brk(TBROK, "No tests to run"); - if (tst_test->max_runtime < -1) { - tst_brk(TBROK, "Invalid runtime value %i", - results->max_runtime); + if (tst_test->timeout < -1) { + tst_brk(TBROK, "Invalid timeout value %i", + tst_test->timeout); } + if (tst_test->runtime < 0) + tst_brk(TBROK, "Invalid runtime value %i", tst_test->runtime); + if (tst_test->tconf_msg) tst_brk(TCONF, "%s", tst_test->tconf_msg); - assert_test_fn(); - - TCID = tid = get_tid(argv); + if (tst_test->supported_archs && !tst_is_on_arch(tst_test->supported_archs)) + tst_brk(TCONF, "This arch '%s' is not supported for test!", tst_arch.name); if (tst_test->sample) tst_test = tst_timer_test_setup(tst_test); + if (tst_test->runs_script) { + tst_test->child_needs_reinit = 1; + tst_test->forks_child = 1; + } + + if (reproducible_env && + (!strcmp(reproducible_env, "1") || !strcmp(reproducible_env, "y"))) + reproducible_output = 1; + + if (quiet_env && + (!strcmp(quiet_env, "1") || !strcmp(quiet_env, "y"))) + quiet_output = 1; + + assert_test_fn(); + + TCID = tcid = get_tcid(argv); + + setup_ipc(); + parse_opts(argc, argv); + if (tdebug_env && (!strcmp(tdebug_env, "1") || !strcmp(tdebug_env, "y"))) { + tst_res(TINFO, "Enabling debug info"); + context->tdebug = 1; + } + if (tst_test->needs_kconfigs && tst_kconfig_check(tst_test->needs_kconfigs)) tst_brk(TCONF, "Aborting due to unsuitable kernel config, see above!"); @@ -1164,10 +1424,7 @@ static void do_setup(int argc, char *argv[]) tst_brk(TCONF, "Test needs to be run as root"); if (tst_test->min_kver) - check_kver(); - - if (tst_test->supported_archs && !tst_is_on_arch(tst_test->supported_archs)) - tst_brk(TCONF, "This arch '%s' is not supported for test!", tst_arch.name); + check_kver(tst_test->min_kver, 1); if (tst_test->skip_in_lockdown && tst_lockdown_enabled() > 0) tst_brk(TCONF, "Kernel is locked down, skipping test"); @@ -1175,15 +1432,18 @@ static void do_setup(int argc, char *argv[]) if (tst_test->skip_in_secureboot && tst_secureboot_enabled() > 0) tst_brk(TCONF, "SecureBoot enabled, skipping test"); - if (tst_test->skip_in_compat && TST_ABI != tst_kernel_bits()) + if (tst_test->skip_in_compat && tst_is_compat_mode()) tst_brk(TCONF, "Not supported in 32-bit compat mode"); + if (tst_test->needs_abi_bits && !tst_abi_bits(tst_test->needs_abi_bits)) + tst_brk(TCONF, "%dbit ABI is not supported", tst_test->needs_abi_bits); + if (tst_test->needs_cmds) { const char *cmd; int i; for (i = 0; (cmd = tst_test->needs_cmds[i]); ++i) - tst_check_cmd(cmd); + tst_check_cmd(cmd, 1); } if (tst_test->needs_drivers) { @@ -1216,8 +1476,6 @@ static void do_setup(int argc, char *argv[]) if (tst_test->hugepages.number) tst_reserve_hugepages(&tst_test->hugepages); - setup_ipc(); - if (tst_test->bufs) tst_buffers_alloc(tst_test->bufs); @@ -1233,6 +1491,15 @@ static void do_setup(int argc, char *argv[]) } } + if (tst_test->ulimit) { + const struct tst_ulimit_val *pvl = tst_test->ulimit; + + while (pvl->resource) { + tst_set_ulimit(pvl); + pvl++; + } + } + if (tst_test->mntpoint) SAFE_MKDIR(tst_test->mntpoint, 0777); @@ -1267,7 +1534,7 @@ static void do_setup(int argc, char *argv[]) if (tst_test->needs_hugetlbfs) prepare_and_mount_hugetlb_fs(); - if (tst_test->needs_device && !mntpoint_mounted) { + if (tst_test->needs_device && !context->mntpoint_mounted) { tdev.dev = tst_acquire_device_(NULL, tst_test->dev_min_size); if (!tdev.dev) @@ -1277,24 +1544,28 @@ static void do_setup(int argc, char *argv[]) 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(); + tdev.fs_type = default_fs_type(); - if (!tst_test->all_filesystems) - prepare_device(); + if (!tst_test->all_filesystems && count_fs_descs() <= 1) { + if (tst_test->filesystems && tst_test->filesystems->mkfs_ver) + tst_check_cmd(tst_test->filesystems->mkfs_ver, 1); + + if (tst_test->filesystems && tst_test->filesystems->min_kver) + check_kver(tst_test->filesystems->min_kver, 1); + + prepare_device(tst_test->filesystems); + } } 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) + if (tst_test->needs_overlay && !context->mntpoint_mounted) tst_brk(TBROK, "tst_test->mntpoint must be mounted"); - if (tst_test->needs_overlay && !ovl_mounted) { + if (tst_test->needs_overlay && !context->ovl_mounted) { SAFE_MOUNT_OVERLAY(); - ovl_mounted = 1; + context->ovl_mounted = 1; } if (tst_test->resource_files) @@ -1314,7 +1585,7 @@ static void do_setup(int argc, char *argv[]) static void do_test_setup(void) { - main_pid = getpid(); + context->main_pid = getpid(); if (!tst_test->all_filesystems && tst_test->skip_filesystems) { long fs_type = tst_fs_type("."); @@ -1334,7 +1605,7 @@ static void do_test_setup(void) if (tst_test->setup) tst_test->setup(); - if (main_pid != tst_getpid()) + if (context->main_pid != tst_getpid()) tst_brk(TBROK, "Runaway child in setup()!"); if (tst_test->caps) @@ -1346,10 +1617,10 @@ static void do_cleanup(void) if (tst_test->needs_cgroup_ctrls) tst_cg_cleanup(); - if (ovl_mounted) + if (context->ovl_mounted) SAFE_UMOUNT(OVL_MNT); - if (mntpoint_mounted) + if (context->mntpoint_mounted) tst_umount(tst_test->mntpoint); if (tst_test->needs_device && tdev.dev) @@ -1371,7 +1642,7 @@ static void do_cleanup(void) static void heartbeat(void) { - if (tst_clock_gettime(CLOCK_MONOTONIC, &tst_start_time)) + if (tst_clock_gettime(CLOCK_MONOTONIC, &context->start_time)) tst_res(TWARN | TERRNO, "tst_clock_gettime() failed"); if (getppid() == 1) { @@ -1397,13 +1668,14 @@ static void run_tests(void) heartbeat(); tst_test->test_all(); - if (tst_getpid() != main_pid) + if (tst_getpid() != context->main_pid) exit(0); tst_reap_children(); if (results_equal(&saved_results, results)) tst_brk(TBROK, "Test haven't reported results!"); + return; } @@ -1412,7 +1684,7 @@ static void run_tests(void) heartbeat(); tst_test->test(i); - if (tst_getpid() != main_pid) + if (tst_getpid() != context->main_pid) exit(0); tst_reap_children(); @@ -1498,6 +1770,7 @@ static volatile sig_atomic_t sigkill_retries; static void alarm_handler(int sig LTP_ATTRIBUTE_UNUSED) { WRITE_MSG("Test timeouted, sending SIGKILL!\n"); + kill(-test_pid, SIGKILL); alarm(5); @@ -1511,7 +1784,7 @@ static void alarm_handler(int sig LTP_ATTRIBUTE_UNUSED) static void heartbeat_handler(int sig LTP_ATTRIBUTE_UNUSED) { - alarm(results->timeout); + alarm(context->overall_time); sigkill_retries = 0; } @@ -1528,18 +1801,15 @@ unsigned int tst_remaining_runtime(void) static struct timespec now; int elapsed; - if (results->max_runtime == TST_UNLIMITED_RUNTIME) - return UINT_MAX; - - if (results->max_runtime == 0) + if (context->runtime == 0) tst_brk(TBROK, "Runtime not set!"); if (tst_clock_gettime(CLOCK_MONOTONIC, &now)) tst_res(TWARN | TERRNO, "tst_clock_gettime() failed"); - elapsed = tst_timespec_diff_ms(now, tst_start_time) / 1000; - if (results->max_runtime > elapsed) - return results->max_runtime - elapsed; + elapsed = tst_timespec_diff_ms(now, context->start_time) / 1000; + if (context->runtime > elapsed) + return context->runtime - elapsed; return 0; } @@ -1552,36 +1822,47 @@ unsigned int tst_multiply_timeout(unsigned int timeout) if (timeout < 1) tst_brk(TBROK, "timeout must to be >= 1! (%d)", timeout); + if (tst_has_slow_kconfig()) + timeout *= 4; + return timeout * timeout_mul; } -static void set_timeout(void) +static void set_overall_timeout(void) { - unsigned int timeout = DEFAULT_TIMEOUT; + unsigned int timeout = DEFAULT_TIMEOUT + tst_test->timeout; - if (results->max_runtime == TST_UNLIMITED_RUNTIME) { - tst_res(TINFO, "Timeout per run is disabled"); + if (tst_test->timeout == TST_UNLIMITED_TIMEOUT) { + tst_res(TINFO, "Test timeout is not limited"); return; } - if (results->max_runtime < 0) { - tst_brk(TBROK, "max_runtime must to be >= -1! (%d)", - results->max_runtime); - } + context->overall_time = tst_multiply_timeout(timeout) + context->runtime; - results->timeout = tst_multiply_timeout(timeout) + results->max_runtime; - - tst_res(TINFO, "Timeout per run is %uh %02um %02us", - results->timeout/3600, (results->timeout%3600)/60, - results->timeout % 60); + tst_res(TINFO, "Overall timeout per run is %uh %02um %02us", + context->overall_time/3600, (context->overall_time%3600)/60, + context->overall_time % 60); } -void tst_set_max_runtime(int max_runtime) +void tst_set_timeout(int timeout) { - results->max_runtime = multiply_runtime(max_runtime); - tst_res(TINFO, "Updating max runtime to %uh %02um %02us", - max_runtime/3600, (max_runtime%3600)/60, max_runtime % 60); - set_timeout(); + int timeout_adj = DEFAULT_TIMEOUT + timeout; + + context->overall_time = tst_multiply_timeout(timeout_adj) + context->runtime; + + tst_res(TINFO, "Overall timeout per run is %uh %02um %02us", + context->overall_time/3600, (context->overall_time%3600)/60, + context->overall_time % 60); + + heartbeat(); +} + +void tst_set_runtime(int runtime) +{ + context->runtime = multiply_runtime(runtime); + tst_res(TINFO, "Updating runtime to %uh %02um %02us", + runtime/3600, (runtime%3600)/60, runtime % 60); + set_overall_timeout(); heartbeat(); } @@ -1592,7 +1873,9 @@ static int fork_testrun(void) SAFE_SIGNAL(SIGINT, sigint_handler); SAFE_SIGNAL(SIGTERM, sigint_handler); - alarm(results->timeout); + alarm(context->overall_time); + + show_failure_hints = 1; test_pid = fork(); if (test_pid < 0) @@ -1622,7 +1905,10 @@ static int fork_testrun(void) tst_res(TINFO, "Killed the leftover descendant processes"); if (WIFEXITED(status) && WEXITSTATUS(status)) - return WEXITSTATUS(status); + tst_brk(TBROK, "Child returned with %i", WEXITSTATUS(status)); + + if (context->abort_flag) + return 0; if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) { tst_res(TINFO, "If you are running on slow machine, " @@ -1636,38 +1922,84 @@ static int fork_testrun(void) return 0; } +static struct tst_fs *lookup_fs_desc(const char *fs_type, int all_filesystems) +{ + struct tst_fs *fs = tst_test->filesystems; + static struct tst_fs empty; + + if (!fs) + goto ret; + + for (; fs->type; fs++) { + + if (!fs->type) + continue; + + if (!strcmp(fs_type, fs->type)) + return fs; + } + +ret: + if (!all_filesystems) + return NULL; + + if (!tst_test->filesystems || tst_test->filesystems[0].type) + return ∅ + + return &tst_test->filesystems[0]; +} + +static int run_tcase_on_fs(struct tst_fs *fs, const char *fs_type) +{ + int ret; + + tst_res(TINFO, "=== Testing on %s ===", fs_type); + tdev.fs_type = fs_type; + + if (fs->mkfs_ver && tst_check_cmd(fs->mkfs_ver, 0)) + return TCONF; + + if (fs->min_kver && check_kver(fs->min_kver, 0)) + return TCONF; + + prepare_device(fs); + + ret = fork_testrun(); + + if (context->mntpoint_mounted) { + tst_umount(tst_test->mntpoint); + context->mntpoint_mounted = 0; + } + + return ret; +} + static int run_tcases_per_fs(void) { int ret = 0; unsigned int i; + bool found_valid_fs = false; const char *const *filesystems = tst_get_supported_fs_types(tst_test->skip_filesystems); if (!filesystems[0]) tst_brk(TCONF, "There are no supported filesystems"); for (i = 0; filesystems[i]; i++) { + struct tst_fs *fs = lookup_fs_desc(filesystems[i], tst_test->all_filesystems); - 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) + if (!fs) continue; - if (ret == 0) - continue; + found_valid_fs = true; + run_tcase_on_fs(fs, filesystems[i]); - do_exit(ret); + if (tst_atomic_load(&context->abort_flag)) + do_exit(0); } + if (!found_valid_fs) + tst_brk(TCONF, "No required filesystems are available"); + return ret; } @@ -1675,43 +2007,46 @@ unsigned int tst_variant; void tst_run_tcases(int argc, char *argv[], struct tst_test *self) { - int ret = 0; unsigned int test_variants = 1; + struct utsname uval; - lib_pid = getpid(); tst_test = self; do_setup(argc, argv); - tst_enable_oom_protection(lib_pid); + tst_enable_oom_protection(context->lib_pid); SAFE_SIGNAL(SIGALRM, alarm_handler); SAFE_SIGNAL(SIGUSR1, heartbeat_handler); tst_res(TINFO, "LTP version: "LTP_VERSION); - if (tst_test->max_runtime) - results->max_runtime = multiply_runtime(tst_test->max_runtime); + uname(&uval); + tst_res(TINFO, "Tested kernel: %s %s %s", uval.release, uval.version, uval.machine); - set_timeout(); + if (tst_test->min_runtime && !tst_test->runtime) + tst_test->runtime = tst_test->min_runtime; + + if (tst_test->runtime) + context->runtime = multiply_runtime(tst_test->runtime); + + set_overall_timeout(); 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(); + if (tst_test->all_filesystems || count_fs_descs() > 1) + run_tcases_per_fs(); else - ret |= fork_testrun(); + fork_testrun(); - if (ret & ~(TCONF)) - goto exit; + if (tst_atomic_load(&context->abort_flag)) + do_exit(0); } -exit: - do_exit(ret); + do_exit(0); } - void tst_flush(void) { int rval; diff --git a/lib/tst_test_macros.c b/lib/tst_test_macros.c new file mode 100644 index 00000000..79d670af --- /dev/null +++ b/lib/tst_test_macros.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + */ + +#include +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" +#include "tst_test_macros.h" + +bool tst_errno_in_set(int err, const int *exp_errs, int exp_errs_cnt) +{ + int i; + + for (i = 0; i < exp_errs_cnt; i++) { + if (err == exp_errs[i]) + return 1; + } + + return 0; +} + +const char *tst_errno_names(char *buf, const int *exp_errs, int exp_errs_cnt) +{ + int i; + char *cb = buf; + + for (i = 0; i < exp_errs_cnt-1; i++) + cb += sprintf(cb, "%s, ", tst_strerrno(exp_errs[i])); + + cb += sprintf(cb, "%s", tst_strerrno(exp_errs[i])); + + *cb = '\0'; + + return buf; +} diff --git a/lib/tst_thread_state.c b/lib/tst_thread_state.c index 6562dfaf..4d04136c 100644 --- a/lib/tst_thread_state.c +++ b/lib/tst_thread_state.c @@ -20,7 +20,7 @@ int tst_thread_state_wait(pid_t tid, const char state, snprintf(proc_path, sizeof(proc_path), "/proc/self/task/%i/stat", tid); for (;;) { - SAFE_FILE_SCANF(proc_path, "%*i %*s %c", &cur_state); + SAFE_FILE_SCANF(proc_path, "%*[^)]%*c %c", &cur_state); if (state == cur_state) break; diff --git a/lib/tst_timer.c b/lib/tst_timer.c index 62d8f908..ecf165ca 100755 --- a/lib/tst_timer.c +++ b/lib/tst_timer.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2015 Cyril Hrubis */ diff --git a/lib/tst_timer_test.c b/lib/tst_timer_test.c index 512edc54..b5a088a1 100755 --- a/lib/tst_timer_test.c +++ b/lib/tst_timer_test.c @@ -454,7 +454,7 @@ static void parse_timer_opts(void) runtime_us += tcases[i].usec * tcases[i].samples; } - tst_set_max_runtime((runtime_us + runtime_us/10)/1000000); + tst_set_runtime((runtime_us + runtime_us/10)/1000000); } struct tst_test *tst_timer_test_setup(struct tst_test *timer_test) diff --git a/lib/tst_tmpdir.c b/lib/tst_tmpdir.c index b73b5c66..6ed2367b 100755 --- a/lib/tst_tmpdir.c +++ b/lib/tst_tmpdir.c @@ -71,7 +71,9 @@ #include #include "test.h" +#include "tst_buffers.h" #include "safe_macros.h" +#include "tst_tmpdir.h" #include "ltp_priv.h" #include "lapi/futex.h" @@ -92,7 +94,7 @@ * Define global variables. */ extern char *TCID; /* defined/initialized in main() */ -static char *TESTDIR = NULL; /* the directory created */ +static char *TESTDIR; /* the directory created */ static char test_start_work_dir[PATH_MAX]; @@ -310,6 +312,9 @@ void tst_tmpdir(void) tst_exit(); } + + tst_resm(TINFO, "Using %s as tmpdir (%s filesystem)", TESTDIR, + tst_fs_type_name(tst_fs_type(NULL, TESTDIR))); } void tst_rmdir(void) @@ -351,3 +356,47 @@ void tst_purge_dir(const char *path) if (purge_dir(path, &err)) tst_brkm(TBROK, NULL, "%s: %s", __func__, err); } + +char *tst_tmpdir_path(void) +{ + static char *tmpdir; + + if (!TESTDIR) + tst_brkm(TBROK, NULL, ".needs_tmpdir must be set!"); + + if (tmpdir) + return tmpdir; + + tmpdir = tst_strdup(TESTDIR); + + return tmpdir; +} + +char *tst_tmpdir_genpath(const char *fmt, ...) +{ + size_t testdir_len, path_len; + va_list va, vac; + char *ret; + + if (!TESTDIR) + tst_brkm(TBROK, NULL, ".needs_tmpdir must be set!"); + + testdir_len = strlen(TESTDIR); + path_len = testdir_len; + + va_start(va, fmt); + va_copy(vac, va); + path_len += vsnprintf(NULL, 0, fmt, va) + 2; + va_end(va); + + ret = tst_alloc(path_len); + + strcpy(ret, TESTDIR); + + ret[testdir_len] = '/'; + + vsprintf(ret + testdir_len + 1, fmt, vac); + va_end(vac); + + return ret; +} diff --git a/libs/libltpipc/Makefile b/libs/ipc/Makefile old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpipc/Makefile rename to libs/ipc/Makefile diff --git a/libs/libltpipc/libipc.c b/libs/ipc/libipc.c old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpipc/libipc.c rename to libs/ipc/libipc.c diff --git a/libs/libltpipc/libmsgctl.c b/libs/ipc/libmsgctl.c old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpipc/libmsgctl.c rename to libs/ipc/libmsgctl.c diff --git a/libs/libltpswap/libswap.c b/libs/libltpswap/libswap.c deleted file mode 100755 index a4427736..00000000 --- a/libs/libltpswap/libswap.c +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. - * Author: Stanislav Kholmanskikh - */ - -#include - -#define TST_NO_DEFAULT_MAIN - -#include "tst_test.h" -#include "libswap.h" -#include "lapi/syscalls.h" - -/* - * Make a swap file - */ -int make_swapfile(const char *swapfile, int safe) -{ - if (!tst_fs_has_free(".", sysconf(_SC_PAGESIZE) * 10, TST_BYTES)) - tst_brk(TBROK, "Insufficient disk space to create swap file"); - - /* create file */ - if (tst_fill_file(swapfile, 0, sysconf(_SC_PAGESIZE), 10) != 0) - tst_brk(TBROK, "Failed to create swapfile"); - - /* make the file swapfile */ - const char *argv[2 + 1]; - argv[0] = "mkswap"; - argv[1] = swapfile; - argv[2] = NULL; - - return tst_cmd(argv, "/dev/null", "/dev/null", safe); -} - -/* - * Check swapon/swapoff support status of filesystems or files - * we are testing on. - */ -void is_swap_supported(const char *filename) -{ - int fibmap = tst_fibmap(filename); - long fs_type = tst_fs_type(filename); - const char *fstype = tst_fs_type_name(fs_type); - - int ret = make_swapfile(filename, 1); - if (ret != 0) { - if (fibmap == 1) - tst_brk(TCONF, "mkswap on %s not supported", fstype); - else - tst_brk(TFAIL, "mkswap on %s failed", fstype); - } - - TEST(tst_syscall(__NR_swapon, filename, 0)); - if (TST_RET == -1) { - if (fibmap == 1 && errno == EINVAL) - tst_brk(TCONF, "Swapfile on %s not implemented", fstype); - else - tst_brk(TFAIL | TTERRNO, "swapon on %s failed", fstype); - } - - TEST(tst_syscall(__NR_swapoff, filename, 0)); - if (TST_RET == -1) - tst_brk(TFAIL | TTERRNO, "swapoff on %s failed", fstype); -} diff --git a/libs/libltpnewipc/Makefile b/libs/newipc/Makefile old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpnewipc/Makefile rename to libs/newipc/Makefile diff --git a/libs/libltpnewipc/libnewipc.c b/libs/newipc/libnewipc.c old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpnewipc/libnewipc.c rename to libs/newipc/libnewipc.c diff --git a/libs/libltpnuma/Makefile b/libs/numa/Makefile old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpnuma/Makefile rename to libs/numa/Makefile diff --git a/libs/libltpnuma/tst_numa.c b/libs/numa/tst_numa.c old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpnuma/tst_numa.c rename to libs/numa/tst_numa.c diff --git a/libs/libltpsigwait/Makefile b/libs/sigwait/Makefile old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpsigwait/Makefile rename to libs/sigwait/Makefile diff --git a/libs/libltpsigwait/sigwait.c b/libs/sigwait/sigwait.c old mode 100755 new mode 100644 similarity index 90% rename from libs/libltpsigwait/sigwait.c rename to libs/sigwait/sigwait.c index 86899954..a9fd62d7 --- a/libs/libltpsigwait/sigwait.c +++ b/libs/sigwait/sigwait.c @@ -286,9 +286,7 @@ void test_bad_address(swi_func sigwaitinfo, int signo, /* let's not get interrupted by our dying child */ SAFE_SIGADDSET(&sigs, SIGCHLD); - TEST(sigprocmask(SIG_SETMASK, &sigs, &oldmask)); - if (TST_RET == -1) - tst_brk(TBROK | TTERRNO, "sigprocmask() failed"); + SAFE_SIGPROCMASK(SIG_SETMASK, &sigs, &oldmask); /* don't wait on a SIGCHLD */ SAFE_SIGDELSET(&sigs, SIGCHLD); @@ -296,19 +294,8 @@ void test_bad_address(swi_func sigwaitinfo, int signo, /* Run a child that will wake us up */ child = create_sig_proc(signo, 1, 0); - TEST(sigwaitinfo(&sigs, (void *)1, NULL)); - if (TST_RET == -1) { - if (TST_ERR == EFAULT) - tst_res(TPASS, "Fault occurred while accessing the buffers"); - else - tst_res(TFAIL | TTERRNO, "Expected error number EFAULT, got"); - } else { - tst_res(TFAIL, "Expected return value -1, got: %ld", TST_RET); - } - - TEST(sigprocmask(SIG_SETMASK, &oldmask, NULL)); - if (TST_RET == -1) - tst_brk(TBROK | TTERRNO, "restoring original signal mask failed"); + TST_EXP_FAIL(sigwaitinfo(&sigs, (void *)1, NULL), EFAULT); + SAFE_SIGPROCMASK(SIG_SETMASK, &oldmask, NULL); SAFE_KILL(child, SIGTERM); SAFE_WAIT(NULL); @@ -359,17 +346,27 @@ void test_bad_address3(swi_func sigwaitinfo, int signo LTP_ATTRIBUTE_UNUSED, enum tst_ts_type type LTP_ATTRIBUTE_UNUSED) { sigset_t sigs; + pid_t pid; + int status; - SAFE_SIGEMPTYSET(&sigs); - TEST(sigwaitinfo(&sigs, NULL, (void *)1)); - if (TST_RET == -1) { - if (TST_ERR == EFAULT) - tst_res(TPASS, "Fault occurred while accessing the buffers"); - else - tst_res(TFAIL | TTERRNO, "Expected error number EFAULT, got"); - } else { - tst_res(TFAIL, "Expected return value -1, got: %ld", TST_RET); + pid = SAFE_FORK(); + if (pid == 0) { + SAFE_SIGEMPTYSET(&sigs); + TST_EXP_FAIL(sigwaitinfo(&sigs, NULL, (void *)1), EFAULT); + _exit(0); } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFEXITED(status) && !WEXITSTATUS(status)) + return; + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "Child killed by expected signal"); + return; + } + + tst_res(TFAIL, "Child %s", tst_strstatus(status)); } static void empty_handler(int sig LTP_ATTRIBUTE_UNUSED) diff --git a/libs/libltpswap/Makefile b/libs/swap/Makefile old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpswap/Makefile rename to libs/swap/Makefile diff --git a/libs/swap/libswap.c b/libs/swap/libswap.c new file mode 100644 index 00000000..e1035550 --- /dev/null +++ b/libs/swap/libswap.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. + * Copyright (c) Linux Test Project, 2014-2024 + * Author: Stanislav Kholmanskikh + */ + +#include +#include +#include +#include +#include +#include + +#define TST_NO_DEFAULT_MAIN +#define DEFAULT_MAX_SWAPFILE 32 +#define BUFSIZE 200 + +#include "tst_test.h" +#include "libswap.h" +#include "lapi/syscalls.h" +#include "tst_kconfig.h" +#include "tst_kvercmp.h" +#include "tst_safe_stdio.h" + +static const char *const swap_supported_fs[] = { + "btrfs", + "ext2", + "ext3", + "ext4", + "xfs", + "vfat", + "exfat", + "ntfs", + NULL +}; + +static void set_nocow_attr(const char *filename) +{ + int fd; + int attrs; + + tst_res(TINFO, "FS_NOCOW_FL attribute set on %s", filename); + + fd = SAFE_OPEN(filename, O_RDONLY); + + SAFE_IOCTL(fd, FS_IOC_GETFLAGS, &attrs); + + attrs |= FS_NOCOW_FL; + + SAFE_IOCTL(fd, FS_IOC_SETFLAGS, &attrs); + + SAFE_CLOSE(fd); +} + +static int prealloc_contiguous_file(const char *path, size_t bs, size_t bcount) +{ + int fd; + + fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0600); + if (fd < 0) + return -1; + + /* Btrfs file need set 'nocow' attribute */ + if (tst_fs_type(path) == TST_BTRFS_MAGIC) + set_nocow_attr(path); + + if (tst_prealloc_size_fd(fd, bs, bcount)) { + close(fd); + unlink(path); + return -1; + } + + if (close(fd) < 0) { + unlink(path); + return -1; + } + + return 0; +} + +static int file_is_contiguous(const char *filename) +{ + int fd, contiguous = 0; + struct fiemap *fiemap; + + if (tst_fibmap(filename) == 0) { + contiguous = 1; + goto out; + } + + if (tst_fs_type(filename) == TST_TMPFS_MAGIC) + goto out; + + fd = SAFE_OPEN(filename, O_RDONLY); + + fiemap = (struct fiemap *)SAFE_MALLOC(sizeof(struct fiemap) + + sizeof(struct fiemap_extent)); + + memset(fiemap, 0, sizeof(struct fiemap) + sizeof(struct fiemap_extent)); + + fiemap->fm_start = 0; + fiemap->fm_length = ~0; + fiemap->fm_flags = 0; + fiemap->fm_extent_count = 1; + + SAFE_IOCTL(fd, FS_IOC_FIEMAP, fiemap); + + /* + * fiemap->fm_mapped_extents != 1: + * This checks if the file does not have exactly one extent. If there are more + * or zero extents, the file is not stored in a single contiguous block. + * + * fiemap->fm_extents[0].fe_logical != 0: + * This checks if the first extent does not start at the logical offset 0 of + * the file. If it doesn't, it indicates that the file's first block of data + * is not at the beginning of the file, which implies non-contiguity. + * + * (fiemap->fm_extents[0].fe_flags & FIEMAP_EXTENT_LAST) != FIEMAP_EXTENT_LAST: + * This checks if the first extent does not have the FIEMAP_EXTENT_LAST flag set. + * If the flag isn't set, it means that this extent is not the last one, suggesting + * that there are more extents and the file is not contiguous. + */ + if (fiemap->fm_mapped_extents != 1 || + fiemap->fm_extents[0].fe_logical != 0 || + (fiemap->fm_extents[0].fe_flags & FIEMAP_EXTENT_LAST) != FIEMAP_EXTENT_LAST) { + + tst_res(TINFO, "File '%s' is not contiguous", filename); + contiguous = 0; + } + + SAFE_CLOSE(fd); + free(fiemap); + +out: + return contiguous; +} + +int make_swapfile(const char *file, const int lineno, + const char *swapfile, unsigned int num, + int safe, enum swapfile_method method) +{ + struct statvfs fs_info; + unsigned long blk_size; + unsigned int blocks = 0; + size_t pg_size = sysconf(_SC_PAGESIZE); + char mnt_path[PATH_MAX]; + + if (statvfs(".", &fs_info) == -1) + tst_brk_(file, lineno, TBROK, "statvfs failed"); + + blk_size = fs_info.f_bsize; + + if (method == SWAPFILE_BY_SIZE) { + tst_res_(file, lineno, TINFO, "create a swapfile size of %u megabytes (MB)", num); + blocks = num * 1024 * 1024 / blk_size; + } else if (method == SWAPFILE_BY_BLKS) { + blocks = num; + tst_res_(file, lineno, TINFO, "create a swapfile with %u block numbers", blocks); + } else { + tst_brk_(file, lineno, TBROK, "Invalid method, please see include/libswap.h"); + } + + /* To guarantee at least one page can be swapped out */ + if (blk_size * blocks < pg_size) { + tst_res_(file, lineno, TWARN, "Swapfile size is less than the system page size. " + "Using page size (%lu bytes) instead of block size (%lu bytes).", + (unsigned long)pg_size, blk_size); + blk_size = pg_size; + } + + if (sscanf(swapfile, "%[^/]", mnt_path) != 1) + tst_brk_(file, lineno, TBROK, "sscanf failed"); + + if (!tst_fs_has_free(mnt_path, blk_size * blocks, TST_BYTES)) + tst_brk_(file, lineno, TCONF, "Insufficient disk space to create swap file"); + + /* create file */ + if (prealloc_contiguous_file(swapfile, blk_size, blocks) != 0) + tst_brk_(file, lineno, TBROK, "Failed to create swapfile"); + + /* Fill the file if needed (specific to old xfs filesystems) */ + if (tst_fs_type(swapfile) == TST_XFS_MAGIC) { + if (tst_fill_file(swapfile, 0, blk_size, blocks) != 0) + tst_brk_(file, lineno, TBROK, "Failed to fill swapfile"); + } + + /* make the file swapfile */ + const char *const argv[] = {"mkswap", swapfile, NULL}; + + return tst_cmd(argv, "/dev/null", "/dev/null", safe ? + TST_CMD_PASS_RETVAL | TST_CMD_TCONF_ON_MISSING : 0); +} + +bool is_swap_supported(const char *filename) +{ + int i, sw_support = 0; + int ret = SAFE_MAKE_SMALL_SWAPFILE(filename); + int fi_contiguous = file_is_contiguous(filename); + long fs_type = tst_fs_type(filename); + const char *fstype = tst_fs_type_name(fs_type); + + if (fs_type == TST_BTRFS_MAGIC && + tst_kvercmp(5, 0, 0) < 0) + tst_brk(TCONF, "Swapfile on Btrfs (kernel < 5.0) not implemented"); + + for (i = 0; swap_supported_fs[i]; i++) { + if (strstr(fstype, swap_supported_fs[i])) { + sw_support = 1; + break; + } + } + + if (ret != 0) { + if (fi_contiguous == 0 && sw_support == 0) { + tst_brk(TCONF, "mkswap on %s not supported", fstype); + } else { + tst_res(TFAIL, "mkswap on %s failed", fstype); + return false; + } + } + + TEST(tst_syscall(__NR_swapon, filename, 0)); + if (TST_RET == -1) { + if (errno == EPERM) { + tst_brk(TCONF, "Permission denied for swapon()"); + } else if (errno == EINVAL && fi_contiguous == 0 && sw_support == 0) { + tst_brk(TCONF, "Swapfile on %s not implemented", fstype); + } else { + tst_res(TFAIL | TTERRNO, "swapon() on %s failed", fstype); + return false; + } + } + + TEST(tst_syscall(__NR_swapoff, filename, 0)); + if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "swapoff on %s failed", fstype); + return false; + } + + return true; +} + +int tst_max_swapfiles(void) +{ + unsigned int swp_migration_num = 0, swp_hwpoison_num = 0, + swp_device_num = 0, swp_pte_marker_num = 0, + swp_swapin_error_num = 0; + struct tst_kconfig_var migration = TST_KCONFIG_INIT("CONFIG_MIGRATION"); + struct tst_kconfig_var memory = TST_KCONFIG_INIT("CONFIG_MEMORY_FAILURE"); + struct tst_kconfig_var device = TST_KCONFIG_INIT("CONFIG_DEVICE_PRIVATE"); + struct tst_kconfig_var marker = TST_KCONFIG_INIT("CONFIG_PTE_MARKER"); + struct tst_kern_exv kvers_marker_migration[] = { + /* RHEL9 kernel has patch 6c287605f and 679d10331 since 5.14.0-179 */ + { "RHEL9", "5.14.0-179" }, + { NULL, NULL}, + }; + + struct tst_kern_exv kvers_marker_migration2[] = { + /* RHEL9 kernel has patch ca92ea3dc5a since 5.14.0-441 */ + { "RHEL9", "5.14.0-441" }, + { NULL, NULL}, + }; + + struct tst_kern_exv kvers_device[] = { + /* SLES12-SP4 has patch 5042db43cc26 since 4.12.14-5.5 */ + { "SLES", "4.12.14-5.5" }, + { NULL, NULL}, + }; + + tst_kconfig_read(&migration, 1); + tst_kconfig_read(&memory, 1); + tst_kconfig_read(&device, 1); + tst_kconfig_read(&marker, 1); + + if (migration.choice == 'y') { + if (tst_kvercmp2(5, 19, 0, kvers_marker_migration) < 0) + swp_migration_num = 2; + else + swp_migration_num = 3; + } + + if (memory.choice == 'y') + swp_hwpoison_num = 1; + + if (device.choice == 'y') { + if (tst_kvercmp2(4, 14, 0, kvers_device) >= 0) + swp_device_num = 2; + if (tst_kvercmp(5, 14, 0) >= 0) + swp_device_num = 4; + if (tst_kvercmp(6, 15, 0) >= 0) + swp_device_num = 3; + } + + if ((marker.choice == 'y' && + tst_kvercmp2(5, 19, 0, kvers_marker_migration) >= 0) + || tst_kvercmp2(6, 2, 0, kvers_marker_migration2) >= 0) { + swp_pte_marker_num = 1; + } + + if ((tst_kvercmp(5, 19, 0) >= 0) && (tst_kvercmp(6, 2, 0) < 0)) + swp_swapin_error_num = 1; + + return DEFAULT_MAX_SWAPFILE - swp_migration_num - swp_hwpoison_num + - swp_device_num - swp_pte_marker_num - swp_swapin_error_num; +} + +int tst_count_swaps(void) +{ + FILE *fp; + int used = -1; + char buf[BUFSIZE]; + + fp = SAFE_FOPEN("/proc/swaps", "r"); + if (fp == NULL) + return -1; + + while (fgets(buf, BUFSIZE, fp) != NULL) + used++; + + SAFE_FCLOSE(fp); + if (used < 0) + tst_brk(TBROK, "can't read /proc/swaps to get used swapfiles resource total"); + + return used; +} diff --git a/libs/libltpuinput/Makefile b/libs/uinput/Makefile old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpuinput/Makefile rename to libs/uinput/Makefile diff --git a/libs/libltpuinput/tst_uinput.c b/libs/uinput/tst_uinput.c old mode 100755 new mode 100644 similarity index 81% rename from libs/libltpuinput/tst_uinput.c rename to libs/uinput/tst_uinput.c index 6dc8a7d4..16e68915 --- a/libs/libltpuinput/tst_uinput.c +++ b/libs/uinput/tst_uinput.c @@ -13,6 +13,7 @@ #include "tst_test.h" #include "tst_uinput.h" +#include "tst_safe_stdio.h" #define VIRTUAL_DEVICE "virtual-device-ltp" @@ -127,6 +128,29 @@ void destroy_input_device(int fd) SAFE_CLOSE(fd); } +static void check_ui_get_sysname_ioctl(int fd) +{ + char sys_name[256]; + char dev_name[256]; + char *path; + + SAFE_IOCTL(fd, UI_GET_SYSNAME(sizeof(sys_name)), sys_name, NULL); + SAFE_ASPRINTF(&path, "/sys/devices/virtual/input/%s/name", sys_name); + + if (FILE_SCANF(path, "%s", dev_name)) { + free(path); + tst_brk(TBROK|TERRNO, "Failed to read '%s'", path); + return; + } + + if (strcmp(VIRTUAL_DEVICE, dev_name)) { + free(path); + tst_brk(TBROK, "ioctl UI_GET_SYSNAME returned wrong name"); + } + + free(path); +} + void create_input_device(int fd) { int nb; @@ -144,8 +168,10 @@ void create_input_device(int fd) SAFE_IOCTL(fd, UI_DEV_CREATE, NULL); for (nb = 100; nb > 0; nb--) { - if (check_device()) + if (check_device()) { + check_ui_get_sysname_ioctl(fd); return; + } usleep(10000); } diff --git a/testcases/network/xinetd/Makefile b/libs/ujson/Makefile old mode 100755 new mode 100644 similarity index 51% rename from testcases/network/xinetd/Makefile rename to libs/ujson/Makefile index b789b2a3..4c850801 --- a/testcases/network/xinetd/Makefile +++ b/libs/ujson/Makefile @@ -1,11 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2009, Cisco Systems Inc. -# Ngie Cooper, July 2009 +# +# Copyright (C) Cyril Hrubis -top_srcdir ?= ../../.. +top_srcdir ?= ../.. include $(top_srcdir)/include/mk/env_pre.mk -INSTALL_TARGETS := xinetd_tests.sh +INTERNAL_LIB := libujson.a +include $(top_srcdir)/include/mk/lib.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/libs/ujson/ujson_common.c b/libs/ujson/ujson_common.c new file mode 100644 index 00000000..63995522 --- /dev/null +++ b/libs/ujson/ujson_common.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2021-2024 Cyril Hrubis + */ + +#include +#include "ujson_common.h" + +void ujson_err_handler(void *err_print_priv, const char *line) +{ + fputs(line, err_print_priv); + putc('\n', err_print_priv); +} + +const char *ujson_type_name(enum ujson_type type) +{ + switch (type) { + case UJSON_VOID: + return "void"; + case UJSON_INT: + return "integer"; + case UJSON_FLOAT: + return "float"; + case UJSON_BOOL: + return "boolean"; + case UJSON_NULL: + return "null"; + case UJSON_STR: + return "string"; + case UJSON_OBJ: + return "object"; + case UJSON_ARR: + return "array"; + default: + return "invalid"; + } +} diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c new file mode 100644 index 00000000..9f86f25b --- /dev/null +++ b/libs/ujson/ujson_reader.c @@ -0,0 +1,1085 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2021-2024 Cyril Hrubis + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ujson_utf.h" +#include "ujson_reader.h" + +static const struct ujson_obj empty = {}; +const struct ujson_obj *ujson_empty_obj = ∅ + +static inline int buf_empty(ujson_reader *buf) +{ + return buf->off >= buf->len; +} + +static int eatws(ujson_reader *buf) +{ + while (!buf_empty(buf)) { + switch (buf->json[buf->off]) { + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + goto ret; + } + + buf->off += 1; + } +ret: + return buf_empty(buf); +} + +static char getb(ujson_reader *buf) +{ + if (buf_empty(buf)) + return 0; + + return buf->json[buf->off++]; +} + +static char peekb_off(ujson_reader *buf, size_t off) +{ + if (buf->off + off >= buf->len) + return 0; + + return buf->json[buf->off + off]; +} + +static char peekb(ujson_reader *buf) +{ + if (buf_empty(buf)) + return 0; + + return buf->json[buf->off]; +} + +static int eatb(ujson_reader *buf, char ch) +{ + if (peekb(buf) != ch) + return 0; + + getb(buf); + return 1; +} + +static int eatb2(ujson_reader *buf, char ch1, char ch2) +{ + if (peekb(buf) != ch1 && peekb(buf) != ch2) + return 0; + + getb(buf); + return 1; +} + +static int eatstr(ujson_reader *buf, const char *str) +{ + while (*str) { + if (!eatb(buf, *str)) + return 0; + str++; + } + + return 1; +} + +static int hex2val(unsigned char b) +{ + switch (b) { + case '0' ... '9': + return b - '0'; + case 'a' ... 'f': + return b - 'a' + 10; + case 'A' ... 'F': + return b - 'A' + 10; + default: + return -1; + } +} + +static int32_t parse_ucode_cp(ujson_reader *buf) +{ + int ret = 0, v, i; + + for (i = 0; i < 4; i++) { + if ((v = hex2val(getb(buf))) < 0) + goto err; + ret *= 16; + ret += v; + } + + return ret; +err: + ujson_err(buf, "Expected four hexadecimal digits"); + return -1; +} + +static unsigned int parse_ucode_esc(ujson_reader *buf, char *str, + size_t off, size_t len) +{ + int32_t ucode = parse_ucode_cp(buf); + + if (ucode < 0) + return 0; + + if (!str) + return ucode; + + if (ujson_utf8_bytes(ucode) + 1 >= len - off) { + ujson_err(buf, "String buffer too short!"); + return 0; + } + + return ujson_to_utf8(ucode, str+off); +} + +static int copy_str(ujson_reader *buf, char *str, size_t len) +{ + size_t pos = 0; + int esc = 0; + unsigned int l; + + eatb(buf, '"'); + + for (;;) { + if (buf_empty(buf)) { + ujson_err(buf, "Unterminated string"); + return 1; + } + + if (!esc && eatb(buf, '"')) { + if (str) + str[pos] = 0; + return 0; + } + + unsigned char b = getb(buf); + + if (b < 0x20) { + if (!peekb(buf)) + ujson_err(buf, "Unterminated string"); + else + ujson_err(buf, "Invalid string character 0x%02x", b); + return 1; + } + + if (!esc && b == '\\') { + esc = 1; + continue; + } + + if (esc) { + switch (b) { + case '"': + case '\\': + case '/': + break; + case 'b': + b = '\b'; + break; + case 'f': + b = '\f'; + break; + case 'n': + b = '\n'; + break; + case 'r': + b = '\r'; + break; + case 't': + b = '\t'; + break; + case 'u': + if (!(l = parse_ucode_esc(buf, str, pos, len))) + return 1; + pos += l; + b = 0; + break; + default: + ujson_err(buf, "Invalid escape \\%c", b); + return 1; + } + esc = 0; + } + + if (str && b) { + if (pos + 1 >= len) { + ujson_err(buf, "String buffer too short!"); + return 1; + } + + str[pos++] = b; + } + } + + return 1; +} + +static int copy_id_str(ujson_reader *buf, char *str, size_t len) +{ + size_t pos = 0; + + if (eatws(buf)) + goto err0; + + if (!eatb(buf, '"')) + goto err0; + + for (;;) { + if (buf_empty(buf)) { + ujson_err(buf, "Unterminated ID string"); + return 1; + } + + if (eatb(buf, '"')) { + str[pos] = 0; + break; + } + + if (pos >= len-1) { + ujson_err(buf, "ID string too long"); + return 1; + } + + str[pos++] = getb(buf); + } + + if (eatws(buf)) + goto err1; + + if (!eatb(buf, ':')) + goto err1; + + return 0; +err0: + ujson_err(buf, "Expected ID string"); + return 1; +err1: + ujson_err(buf, "Expected ':' after ID string"); + return 1; +} + +static int is_digit(char b) +{ + switch (b) { + case '0' ... '9': + return 1; + default: + return 0; + } +} + +static int get_int(ujson_reader *buf, struct ujson_val *res) +{ + long val = 0; + int sign = 1; + + if (eatb(buf, '-')) { + sign = -1; + if (!is_digit(peekb(buf))) { + ujson_err(buf, "Expected digit(s)"); + return 1; + } + } + + if (peekb(buf) == '0' && is_digit(peekb_off(buf, 1))) { + ujson_err(buf, "Leading zero in number!"); + return 1; + } + + while (is_digit(peekb(buf))) { + val *= 10; + val += getb(buf) - '0'; + //TODO: overflow? + } + + if (sign < 0) + val = -val; + + res->val_int = val; + res->val_float = val; + + return 0; +} + +static int eat_digits(ujson_reader *buf) +{ + if (!is_digit(peekb(buf))) { + ujson_err(buf, "Expected digit(s)"); + return 1; + } + + while (is_digit(peekb(buf))) + getb(buf); + + return 0; +} + +static int get_float(ujson_reader *buf, struct ujson_val *res) +{ + off_t start = buf->off; + + eatb(buf, '-'); + + if (peekb(buf) == '0' && is_digit(peekb_off(buf, 1))) { + ujson_err(buf, "Leading zero in float"); + return 1; + } + + if (eat_digits(buf)) + return 1; + + switch (getb(buf)) { + case '.': + if (eat_digits(buf)) + return 1; + + if (!eatb2(buf, 'e', 'E')) + break; + + /* fallthrough */ + case 'e': + case 'E': + eatb2(buf, '+', '-'); + + if (eat_digits(buf)) + return 1; + break; + } + + size_t len = buf->off - start; + char tmp[len+1]; + + memcpy(tmp, buf->json + start, len); + + tmp[len] = 0; + + res->val_float = strtod(tmp, NULL); + + return 0; +} + +static int get_bool(ujson_reader *buf, struct ujson_val *res) +{ + switch (peekb(buf)) { + case 'f': + if (!eatstr(buf, "false")) { + ujson_err(buf, "Expected 'false'"); + return 1; + } + + res->val_bool = 0; + break; + case 't': + if (!eatstr(buf, "true")) { + ujson_err(buf, "Expected 'true'"); + return 1; + } + + res->val_bool = 1; + break; + } + + return 0; +} + +static int get_null(ujson_reader *buf) +{ + if (!eatstr(buf, "null")) { + ujson_err(buf, "Expected 'null'"); + return 1; + } + + return 0; +} + +int ujson_obj_skip(ujson_reader *buf) +{ + struct ujson_val res = {}; + + UJSON_OBJ_FOREACH(buf, &res) { + switch (res.type) { + case UJSON_OBJ: + if (ujson_obj_skip(buf)) + return 1; + break; + case UJSON_ARR: + if (ujson_arr_skip(buf)) + return 1; + break; + default: + break; + } + } + + return 0; +} + +int ujson_arr_skip(ujson_reader *buf) +{ + struct ujson_val res = {}; + + UJSON_ARR_FOREACH(buf, &res) { + switch (res.type) { + case UJSON_OBJ: + if (ujson_obj_skip(buf)) + return 1; + break; + case UJSON_ARR: + if (ujson_arr_skip(buf)) + return 1; + break; + default: + break; + } + } + + return 0; +} + +static enum ujson_type next_num_type(ujson_reader *buf) +{ + size_t off = 0; + + for (;;) { + char b = peekb_off(buf, off++); + + switch (b) { + case 0: + case ',': + return UJSON_INT; + case '.': + case 'e': + case 'E': + return UJSON_FLOAT; + } + } + + return UJSON_VOID; +} + +enum ujson_type ujson_next_type(ujson_reader *buf) +{ + if (eatws(buf)) { + ujson_err(buf, "Unexpected end"); + return UJSON_VOID; + } + + char b = peekb(buf); + + switch (b) { + case '{': + return UJSON_OBJ; + case '[': + return UJSON_ARR; + case '"': + return UJSON_STR; + case '-': + case '0' ... '9': + return next_num_type(buf); + case 'f': + case 't': + return UJSON_BOOL; + break; + case 'n': + return UJSON_NULL; + break; + default: + ujson_err(buf, "Expected object, array, number or string"); + return UJSON_VOID; + } +} + +enum ujson_type ujson_reader_start(ujson_reader *buf) +{ + enum ujson_type type = ujson_next_type(buf); + + switch (type) { + case UJSON_ARR: + case UJSON_OBJ: + case UJSON_VOID: + break; + default: + ujson_err(buf, "JSON can start only with array or object"); + type = UJSON_VOID; + break; + } + + return type; +} + +static int get_value(ujson_reader *buf, struct ujson_val *res) +{ + int ret = 0; + + res->type = ujson_next_type(buf); + + switch (res->type) { + case UJSON_STR: + if (copy_str(buf, res->buf, res->buf_size)) { + res->type = UJSON_VOID; + return 0; + } + res->val_str = res->buf; + return 1; + case UJSON_INT: + ret = get_int(buf, res); + break; + case UJSON_FLOAT: + ret = get_float(buf, res); + break; + case UJSON_BOOL: + ret = get_bool(buf, res); + break; + case UJSON_NULL: + ret = get_null(buf); + break; + case UJSON_VOID: + return 0; + case UJSON_ARR: + case UJSON_OBJ: + buf->sub_off = buf->off; + return 1; + } + + if (ret) { + res->type = UJSON_VOID; + return 0; + } + + return 1; +} + +static int pre_next(ujson_reader *buf, struct ujson_val *res) +{ + if (!eatb(buf, ',')) { + ujson_err(buf, "Expected ','"); + res->type = UJSON_VOID; + return 1; + } + + if (eatws(buf)) { + ujson_err(buf, "Unexpected end"); + res->type = UJSON_VOID; + return 1; + } + + return 0; +} + +static int check_end(ujson_reader *buf, struct ujson_val *res, char b) +{ + if (eatws(buf)) { + ujson_err(buf, "Unexpected end"); + return 1; + } + + if (eatb(buf, b)) { + res->type = UJSON_VOID; + eatws(buf); + eatb(buf, 0); + buf->depth--; + return 1; + } + + return 0; +} + +/* + * This is supposed to return a pointer to a string stored as a first member of + * a structure given an array. + * + * e.g. + * + * struct foo { + * const char *key; + * ... + * }; + * + * const struct foo bar[10] = {...}; + * + * // Returns a pointer to the key string in a second structure in bar[]. + * const char *key = list_elem(bar, sizeof(struct foo), 1); + */ +static inline const char *list_elem(const void *arr, size_t memb_size, size_t idx) +{ + return *(const char**)(arr + idx * memb_size); +} + +size_t ujson_lookup(const void *arr, size_t memb_size, size_t list_len, + const char *key) +{ + size_t l = 0; + size_t r = list_len-1; + size_t mid = -1; + + if (!list_len) + return (size_t)-1; + + while (r - l > 1) { + mid = (l+r)/2; + + int ret = strcmp(list_elem(arr, memb_size, mid), key); + if (!ret) + return mid; + + if (ret < 0) + l = mid; + else + r = mid; + } + + if (r != mid && !strcmp(list_elem(arr, memb_size, r), key)) + return r; + + if (l != mid && !strcmp(list_elem(arr, memb_size, l), key)) + return l; + + return -1; +} + +static int skip_obj_val(ujson_reader *buf) +{ + struct ujson_val dummy = {}; + + if (!get_value(buf, &dummy)) + return 0; + + switch (dummy.type) { + case UJSON_OBJ: + return !ujson_obj_skip(buf); + case UJSON_ARR: + return !ujson_arr_skip(buf); + default: + return 1; + } +} + +static int obj_next(ujson_reader *buf, struct ujson_val *res) +{ + if (copy_id_str(buf, res->id, sizeof(res->id))) + return 0; + + return get_value(buf, res); +} + +static int obj_pre_next(ujson_reader *buf, struct ujson_val *res) +{ + if (ujson_reader_err(buf)) + return 1; + + if (check_end(buf, res, '}')) + return 1; + + if (pre_next(buf, res)) + return 1; + + return 0; +} + +static int obj_next_filter(ujson_reader *buf, struct ujson_val *res, + const struct ujson_obj *obj, const struct ujson_obj *ign) +{ + const struct ujson_obj_attr *attr; + + for (;;) { + if (copy_id_str(buf, res->id, sizeof(res->id))) + return 0; + + res->idx = obj ? ujson_obj_lookup(obj, res->id) : (size_t)-1; + + if (res->idx != (size_t)-1) { + if (!get_value(buf, res)) + return 0; + + attr = &obj->attrs[res->idx]; + + if (attr->type == UJSON_VOID) + return 1; + + if (attr->type == res->type) + return 1; + + if (attr->type == UJSON_FLOAT && + res->type == UJSON_INT) + return 1; + + ujson_warn(buf, "Wrong '%s' type expected %s", + attr->key, ujson_type_name(attr->type)); + } else { + if (!skip_obj_val(buf)) + return 0; + + if (ign && ujson_obj_lookup(ign, res->id) == (size_t)-1) + ujson_warn(buf, "Unexpected key '%s'", res->id); + } + + if (obj_pre_next(buf, res)) + return 0; + } +} + +static int check_err(ujson_reader *buf, struct ujson_val *res) +{ + if (ujson_reader_err(buf)) { + res->type = UJSON_VOID; + return 1; + } + + return 0; +} + +int ujson_obj_next_filter(ujson_reader *buf, struct ujson_val *res, + const struct ujson_obj *obj, const struct ujson_obj *ign) +{ + if (check_err(buf, res)) + return 0; + + if (obj_pre_next(buf, res)) + return 0; + + return obj_next_filter(buf, res, obj, ign); +} + +int ujson_obj_next(ujson_reader *buf, struct ujson_val *res) +{ + if (check_err(buf, res)) + return 0; + + if (obj_pre_next(buf, res)) + return 0; + + return obj_next(buf, res); +} + +static int any_first(ujson_reader *buf, char b) +{ + if (eatws(buf)) { + ujson_err(buf, "Unexpected end"); + return 1; + } + + if (!eatb(buf, b)) { + ujson_err(buf, "Expected '%c'", b); + return 1; + } + + buf->depth++; + + if (buf->depth > buf->max_depth) { + ujson_err(buf, "Recursion too deep"); + return 1; + } + + return 0; +} + +int ujson_obj_first_filter(ujson_reader *buf, struct ujson_val *res, + const struct ujson_obj *obj, const struct ujson_obj *ign) +{ + if (check_err(buf, res)) + return 0; + + if (any_first(buf, '{')) + return 0; + + if (check_end(buf, res, '}')) + return 0; + + return obj_next_filter(buf, res, obj, ign); +} + +int ujson_obj_first(ujson_reader *buf, struct ujson_val *res) +{ + if (check_err(buf, res)) + return 0; + + if (any_first(buf, '{')) + return 0; + + if (check_end(buf, res, '}')) + return 0; + + return obj_next(buf, res); +} + +static int arr_next(ujson_reader *buf, struct ujson_val *res) +{ + return get_value(buf, res); +} + +int ujson_arr_first(ujson_reader *buf, struct ujson_val *res) +{ + if (check_err(buf, res)) + return 0; + + if (any_first(buf, '[')) + return 0; + + if (check_end(buf, res, ']')) + return 0; + + return arr_next(buf, res); +} + +int ujson_arr_next(ujson_reader *buf, struct ujson_val *res) +{ + if (check_err(buf, res)) + return 0; + + if (check_end(buf, res, ']')) + return 0; + + if (pre_next(buf, res)) + return 0; + + return arr_next(buf, res); +} + +static void ujson_err_va(ujson_reader *buf, const char *fmt, va_list va) +{ + vsnprintf(buf->err, UJSON_ERR_MAX, fmt, va); +} + +void ujson_err(ujson_reader *buf, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + ujson_err_va(buf, fmt, va); + va_end(va); +} + +static void vprintf_line(ujson_reader *buf, const char *fmt, va_list va) +{ + char line[UJSON_ERR_MAX+1]; + + vsnprintf(line, sizeof(line), fmt, va); + + line[UJSON_ERR_MAX] = 0; + + buf->err_print(buf->err_print_priv, line); +} + +static void printf_line(ujson_reader *buf, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vprintf_line(buf, fmt, va); + va_end(va); +} + +static void printf_json_line(ujson_reader *buf, size_t line_nr, const char *buf_pos) +{ + char line[UJSON_ERR_MAX+1]; + size_t plen, i; + + plen = sprintf(line, "%03zu: ", line_nr); + + for (i = 0; i < UJSON_ERR_MAX-plen && buf_pos[i] && buf_pos[i] != '\n'; i++) + line[i+plen] = buf_pos[i]; + + line[i+plen] = 0; + + buf->err_print(buf->err_print_priv, line); +} + +static void print_arrow(ujson_reader *buf, const char *buf_pos, size_t count) +{ + char line[count + 7]; + size_t i; + + /* The '000: ' prefix */ + for (i = 0; i <= 5; i++) + line[i] = ' '; + + for (i = 0; i < count; i++) + line[i+5] = buf_pos[i] == '\t' ? '\t' : ' '; + + line[count+5] = '^'; + line[count+6] = 0; + + buf->err_print(buf->err_print_priv, line); +} + +#define ERR_LINES 10 + +#define MIN(A, B) ((A < B) ? (A) : (B)) + +static void print_snippet(ujson_reader *buf, const char *type) +{ + ssize_t i; + const char *lines[ERR_LINES] = {}; + size_t cur_line = 0; + size_t cur_off = 0; + size_t last_off = buf->off; + + for (;;) { + lines[(cur_line++) % ERR_LINES] = buf->json + cur_off; + + while (cur_off < buf->len && buf->json[cur_off] != '\n') + cur_off++; + + if (cur_off >= buf->off) + break; + + cur_off++; + last_off = buf->off - cur_off; + } + + printf_line(buf, "%s at line %03zu", type, cur_line); + buf->err_print(buf->err_print_priv, ""); + + size_t idx = 0; + + for (i = MIN(ERR_LINES, cur_line); i > 0; i--) { + idx = (cur_line - i) % ERR_LINES; + printf_json_line(buf, cur_line - i + 1, lines[idx]); + } + + print_arrow(buf, lines[idx], last_off); +} + +void ujson_err_print(ujson_reader *buf) +{ + if (!buf->err_print) + return; + + print_snippet(buf, "Parse error"); + buf->err_print(buf->err_print_priv, buf->err); +} + +void ujson_warn(ujson_reader *buf, const char *fmt, ...) +{ + va_list va; + + if (buf->flags & UJSON_READER_STRICT) { + va_start(va, fmt); + ujson_err_va(buf, fmt, va); + va_end(va); + return; + } + + if (!buf->err_print) + return; + + print_snippet(buf, "Warning"); + + va_start(va, fmt); + vprintf_line(buf, fmt, va); + va_end(va); +} + +void ujson_print(void *err_print_priv, const char *line) +{ + fputs(line, err_print_priv); + putc('\n', err_print_priv); +} + +ujson_reader *ujson_reader_load(const char *path) +{ + int fd = open(path, O_RDONLY); + ujson_reader *ret; + ssize_t res; + off_t len, off = 0; + + if (fd < 0) + return NULL; + + len = lseek(fd, 0, SEEK_END); + if (len == (off_t)-1) { + fprintf(stderr, "lseek() failed\n"); + goto err0; + } + + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { + fprintf(stderr, "lseek() failed\n"); + goto err0; + } + + ret = malloc(sizeof(ujson_reader) + len + 1); + if (!ret) { + fprintf(stderr, "malloc() failed\n"); + goto err0; + } + + memset(ret, 0, sizeof(*ret)); + + ret->buf[len] = 0; + ret->len = len; + ret->max_depth = UJSON_RECURSION_MAX; + ret->json = ret->buf; + ret->err_print = UJSON_ERR_PRINT; + ret->err_print_priv = UJSON_ERR_PRINT_PRIV; + + while (off < len) { + res = read(fd, ret->buf + off, len - off); + if (res < 0) { + fprintf(stderr, "read() failed\n"); + goto err1; + } + + off += res; + } + + close(fd); + + return ret; +err1: + free(ret); +err0: + close(fd); + return NULL; +} + +void ujson_reader_finish(ujson_reader *self) +{ + if (ujson_reader_err(self)) + ujson_err_print(self); + else if (!ujson_reader_consumed(self)) { + ujson_warn(self, "Garbage after JSON string!"); + + if (ujson_reader_err(self)) + ujson_err_print(self); + } +} + +void ujson_reader_free(ujson_reader *buf) +{ + free(buf); +} + +ujson_val *ujson_val_alloc(size_t buf_size) +{ + buf_size = buf_size == 0 ? 4096 : buf_size; + ujson_val *ret; + + ret = malloc(sizeof(ujson_val) + buf_size); + if (!ret) + return NULL; + + memset(ret, 0, sizeof(ujson_val) + buf_size); + + ret->buf = ret->buf__; + ret->buf_size = buf_size; + + return ret; +} + +void ujson_val_free(ujson_val *self) +{ + free(self); +} diff --git a/libs/ujson/ujson_utf.c b/libs/ujson/ujson_utf.c new file mode 100644 index 00000000..2c08a39a --- /dev/null +++ b/libs/ujson/ujson_utf.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2022-2024 Cyril Hrubis + */ + +#include +#include + +int8_t ujson_utf8_next_chsz(const char *str, size_t off) +{ + char ch = str[off]; + uint8_t len = 0; + + if (!ch) + return 0; + + if (UJSON_UTF8_IS_ASCII(ch)) + return 1; + + if (UJSON_UTF8_IS_2BYTE(ch)) { + len = 2; + goto ret; + } + + if (UJSON_UTF8_IS_3BYTE(ch)) { + len = 3; + goto ret; + } + + if (UJSON_UTF8_IS_4BYTE(ch)) { + len = 4; + goto ret; + } + + return -1; +ret: + if (!UJSON_UTF8_IS_NBYTE(str[off+1])) + return -1; + + if (len > 2 && !UJSON_UTF8_IS_NBYTE(str[off+2])) + return -1; + + if (len > 3 && !UJSON_UTF8_IS_NBYTE(str[off+3])) + return -1; + + return len; +} + +int8_t ujson_utf8_prev_chsz(const char *str, size_t off) +{ + char ch; + + if (!off) + return 0; + + ch = str[--off]; + + if (UJSON_UTF8_IS_ASCII(ch)) + return 1; + + if (!UJSON_UTF8_IS_NBYTE(ch)) + return -1; + + if (off < 1) + return -1; + + ch = str[--off]; + + if (UJSON_UTF8_IS_2BYTE(ch)) + return 2; + + if (!UJSON_UTF8_IS_NBYTE(ch)) + return -1; + + if (off < 1) + return -1; + + ch = str[--off]; + + if (UJSON_UTF8_IS_3BYTE(ch)) + return 3; + + if (!UJSON_UTF8_IS_NBYTE(ch)) + return -1; + + if (off < 1) + return -1; + + ch = str[--off]; + + if (UJSON_UTF8_IS_4BYTE(ch)) + return 4; + + return -1; +} + +size_t ujson_utf8_strlen(const char *str) +{ + size_t cnt = 0; + + while (ujson_utf8_next(&str)) + cnt++; + + return cnt; +} diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c new file mode 100644 index 00000000..c86a3b2a --- /dev/null +++ b/libs/ujson/ujson_writer.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2021-2024 Cyril Hrubis + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ujson_utf.h" +#include "ujson_writer.h" + +static inline int get_depth_bit(ujson_writer *self, char *mask) +{ + int depth = self->depth - 1; + + if (depth < 0) + return -1; + + return !!(mask[depth/8] & (1<<(depth%8))); +} + +static inline void set_depth_bit(ujson_writer *self, int val) +{ + if (val) + self->depth_type[self->depth/8] |= (1<<(self->depth%8)); + else + self->depth_type[self->depth/8] &= ~(1<<(self->depth%8)); + + self->depth_first[self->depth/8] |= (1<<(self->depth%8)); + + self->depth++; +} + +static inline void clear_depth_bit(ujson_writer *self) +{ + self->depth--; +} + +static inline int in_arr(ujson_writer *self) +{ + return !get_depth_bit(self, self->depth_type); +} + +static inline int in_obj(ujson_writer *self) +{ + return get_depth_bit(self, self->depth_type); +} + +static inline void clear_depth_first(ujson_writer *self) +{ + int depth = self->depth - 1; + + self->depth_first[depth/8] &= ~(1<<(depth%8)); +} + +static inline int is_first(ujson_writer *self) +{ + int ret = get_depth_bit(self, self->depth_first); + + if (ret == 1) + clear_depth_first(self); + + return ret; +} + +static inline void err(ujson_writer *buf, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vsnprintf(buf->err, UJSON_ERR_MAX, fmt, va); + va_end(va); +} + +static inline int is_err(ujson_writer *buf) +{ + return buf->err[0]; +} + +static inline int out(ujson_writer *self, const char *buf, size_t len) +{ + return self->out(self, buf, len); +} + +static inline int out_str(ujson_writer *self, const char *str) +{ + return out(self, str, strlen(str)); +} + +static inline int out_ch(ujson_writer *self, char ch) +{ + return out(self, &ch, 1); +} + +#define ESC_FLUSH(esc_char) do {\ + out(self, val, i); \ + val += i + 1; \ + i = 0; \ + out_str(self, esc_char); \ +} while (0) + +static inline int out_esc_str(ujson_writer *self, const char *val) +{ + if (out_ch(self, '"')) + return 1; + + size_t i = 0; + int8_t next_chsz; + + do { + next_chsz = ujson_utf8_next_chsz(val, i); + + if (next_chsz == 1) { + switch (val[i]) { + case '\"': + ESC_FLUSH("\\\""); + break; + case '\\': + ESC_FLUSH("\\\\"); + break; + case '/': + ESC_FLUSH("\\/"); + break; + case '\b': + ESC_FLUSH("\\b"); + break; + case '\f': + ESC_FLUSH("\\f"); + break; + case '\n': + ESC_FLUSH("\\n"); + break; + case '\r': + ESC_FLUSH("\\r"); + break; + case '\t': + ESC_FLUSH("\\t"); + break; + default: + i += next_chsz; + } + } else { + i += next_chsz; + } + } while (next_chsz); + + if (i) { + if (out(self, val, i)) + return 1; + } + + if (out_ch(self, '"')) + return 1; + + return 0; +} + +static int do_padd(ujson_writer *self) +{ + unsigned int i; + + for (i = 0; i < self->depth; i++) { + if (out_ch(self, ' ')) + return 1; + } + + return 0; +} + +static int newline(ujson_writer *self) +{ + if (out_ch(self, '\n')) + return 0; + + if (do_padd(self)) + return 1; + + return 0; +} + +static int add_common(ujson_writer *self, const char *id) +{ + if (is_err(self)) + return 1; + + if (!self->depth) { + err(self, "Object/Array has to be started first"); + return 1; + } + + if (in_arr(self)) { + if (id) { + err(self, "Array entries can't have id"); + return 1; + } + } else { + if (!id) { + err(self, "Object entries must have id"); + return 1; + } + } + + if (!is_first(self) && out_ch(self, ',')) + return 1; + + if (self->depth && newline(self)) + return 1; + + if (id) { + if (out_esc_str(self, id)) + return 1; + + if (out_str(self, ": ")) + return 1; + } + + return 0; +} + +int ujson_obj_start(ujson_writer *self, const char *id) +{ + if (self->depth >= UJSON_RECURSION_MAX) + return 1; + + if (!self->depth && id) { + err(self, "Top level object cannot have id"); + return 1; + } + + if (self->depth && add_common(self, id)) + return 1; + + if (out_ch(self, '{')) + return 1; + + set_depth_bit(self, 1); + + return 0; +} + +int ujson_obj_finish(ujson_writer *self) +{ + if (is_err(self)) + return 1; + + if (!in_obj(self)) { + err(self, "Not in object!"); + return 1; + } + + int first = is_first(self); + + clear_depth_bit(self); + + if (!first) + newline(self); + + return out_ch(self, '}'); +} + +int ujson_arr_start(ujson_writer *self, const char *id) +{ + if (self->depth >= UJSON_RECURSION_MAX) { + err(self, "Recursion too deep"); + return 1; + } + + if (!self->depth && id) { + err(self, "Top level array cannot have id"); + return 1; + } + + if (self->depth && add_common(self, id)) + return 1; + + if (out_ch(self, '[')) + return 1; + + set_depth_bit(self, 0); + + return 0; +} + +int ujson_arr_finish(ujson_writer *self) +{ + if (is_err(self)) + return 1; + + if (!in_arr(self)) { + err(self, "Not in array!"); + return 1; + } + + int first = is_first(self); + + clear_depth_bit(self); + + if (!first) + newline(self); + + return out_ch(self, ']'); +} + +int ujson_null_add(ujson_writer *self, const char *id) +{ + if (add_common(self, id)) + return 1; + + return out_str(self, "null"); +} + +int ujson_int_add(ujson_writer *self, const char *id, long val) +{ + char buf[64]; + + if (add_common(self, id)) + return 1; + + snprintf(buf, sizeof(buf), "%li", val); + + return out_str(self, buf); +} + +int ujson_bool_add(ujson_writer *self, const char *id, int val) +{ + if (add_common(self, id)) + return 1; + + if (val) + return out_str(self, "true"); + else + return out_str(self, "false"); +} + +int ujson_str_add(ujson_writer *self, const char *id, const char *val) +{ + if (add_common(self, id)) + return 1; + + if (out_esc_str(self, val)) + return 1; + + return 0; +} + +int ujson_float_add(ujson_writer *self, const char *id, double val) +{ + char buf[64]; + + if (add_common(self, id)) + return 1; + + snprintf(buf, sizeof(buf), "%lg", val); + + return out_str(self, buf); +} + +int ujson_writer_finish(ujson_writer *self) +{ + if (is_err(self)) + goto err; + + if (self->depth) { + err(self, "Objects and/or Arrays not finished"); + goto err; + } + + if (newline(self)) + return 1; + + return 0; +err: + if (self->err_print) + self->err_print(self->err_print_priv, self->err); + + return 1; +} + +struct json_writer_file { + int fd; + size_t buf_used; + char buf[1024]; +}; + +static int out_writer_file_write(ujson_writer *self, int fd, const char *buf, ssize_t buf_len) +{ + do { + ssize_t ret = write(fd, buf, buf_len); + if (ret <= 0) { + err(self, "Failed to write to a file"); + return 1; + } + + if (ret > buf_len) { + err(self, "Wrote more bytes than requested?!"); + return 1; + } + + buf_len -= ret; + } while (buf_len); + + return 0; +} + +static int out_writer_file(ujson_writer *self, const char *buf, size_t buf_len) +{ + struct json_writer_file *writer_file = self->out_priv; + size_t buf_size = sizeof(writer_file->buf); + size_t buf_avail = buf_size - writer_file->buf_used; + + if (buf_len > buf_size/4) + return out_writer_file_write(self, writer_file->fd, buf, buf_len); + + if (buf_len >= buf_avail) { + if (out_writer_file_write(self, writer_file->fd, + writer_file->buf, writer_file->buf_used)) + return 1; + + memcpy(writer_file->buf, buf, buf_len); + writer_file->buf_used = buf_len; + return 0; + } + + memcpy(writer_file->buf + writer_file->buf_used, buf, buf_len); + writer_file->buf_used += buf_len; + + return 0; +} + +int ujson_writer_file_close(ujson_writer *self) +{ + struct json_writer_file *writer_file = self->out_priv; + int saved_errno = 0; + + if (writer_file->buf_used) { + if (out_writer_file_write(self, writer_file->fd, + writer_file->buf, writer_file->buf_used)) + + saved_errno = errno; + } + + if (close(writer_file->fd)) { + if (!saved_errno) + saved_errno = errno; + } + + free(self); + + if (saved_errno) { + errno = saved_errno; + return 1; + } + + return 0; +} + +ujson_writer *ujson_writer_file_open(const char *path) +{ + ujson_writer *ret; + struct json_writer_file *writer_file; + + ret = malloc(sizeof(ujson_writer) + sizeof(struct json_writer_file)); + if (!ret) + return NULL; + + writer_file = (void*)ret + sizeof(ujson_writer); + + writer_file->fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0664); + if (!writer_file->fd) { + free(ret); + return NULL; + } + + writer_file->buf_used = 0; + + memset(ret, 0, sizeof(*ret)); + + ret->err_print = UJSON_ERR_PRINT; + ret->err_print_priv = UJSON_ERR_PRINT_PRIV; + ret->out = out_writer_file; + ret->out_priv = writer_file; + + return ret; +} diff --git a/libs/libltpvdso/Makefile b/libs/vdso/Makefile old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpvdso/Makefile rename to libs/vdso/Makefile diff --git a/libs/libltpvdso/README b/libs/vdso/README old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpvdso/README rename to libs/vdso/README diff --git a/libs/libltpvdso/parse_vdso.c b/libs/vdso/parse_vdso.c old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpvdso/parse_vdso.c rename to libs/vdso/parse_vdso.c diff --git a/libs/libltpvdso/vdso_helpers.c b/libs/vdso/vdso_helpers.c old mode 100755 new mode 100644 similarity index 100% rename from libs/libltpvdso/vdso_helpers.c rename to libs/vdso/vdso_helpers.c diff --git a/ltpmenu b/ltpmenu deleted file mode 100755 index 38d38c0c..00000000 --- a/ltpmenu +++ /dev/null @@ -1,548 +0,0 @@ -#!/bin/bash -################################################################################ -## ## -## Copyright (c) International Business Machines Corp., 2001 ## -## ## -## 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 ## -## ## -################################################################################ -# -# File: runltp -# -# Description: This program is a Graphical User Interface (GUI) -# Control Centre for LTP. The Control Centre provides -# functionality to Compile, Execute and View Results of -# LTP test cases. -# -# Author: Manoj Iyer - manjo@mail.utexas.edu -# -# Thanks: Jim Choate - For suggesting the use of dialog command. -# -# History: March 26 2003 - Created. -# -# March 28 2003 - Removed gauges and put make commands in foreground. -# Robbie Williamson - robbiew@us.ibm.com -# -# March 31 2003 - Made scenario menu creation dynamic and code -# to pull the test descriptions from the scenario files. -# Robbie Williamson - robbiew@us.ibm.com -# -# April 17 2003 - Added menu selection to list contents of selected -# scenario file. -# Robbie Williamson - robbiew@us.ibm.com -# -# April 23 2003 - Added PID to results filename. -# - Added code to allow users to redirect output and -# specify test execution duration. -# Robbie Williamson - robbiew@us.ibm.com -# -# April 30, 2003 - Recoded results display to allow selection -# of results file. -# - Created variable to hold results filename -# - Added time to results filename. -# Function: cleanup -# -# Description: Remove all temporary files created by this program. Cleanup -# always called on program exit. -# -# Input: NONE -# -# Output: NONE -cleanup() -{ - rm -f /tmp/runltp.* -} - - -# Function: display_info_msg -# -# Description: Displays informational messages window. This window may -# may be used to display information like errors, instructions -# etc to the user. The window is dismissed when the user hits -# the [ENTER] key. -# -# Input: $1 - Title the needs to be displayed on the window. -# eg: ERROR: Compiling LTP -# $2 - Message text. -# -# Output: Information message window. -display_info_msg() -{ - dialog --backtitle "Linux Test Project Control Centre" \ - --title " $1 " \ - --msgbox " $2 " 10 70 - return $? -} - - -# Function: compile_ltp -# -# Description: Checks for commands that are pre-reqs for compiling and -# installing LTP. It displays a confirmation window inorder to -# confirm the choice made by the user. -# -# Calls: do_make_clean() -# do_make() -# do_make_install() -# -# Input: NONE -# -# Output: Confirmation window. -compile_ltp() -{ - dialog --backtitle "Linux Test Project Control Centre" \ - --title "Compiling LTP testsuite"\ - --yesno "This will compile all the test cases in\ - LTP test suite and place the executables\ - in testcases/bin directory. Do\ - you wish to continue ??" 7 70 || RC=$? - case $RC in - 0) \ - for cmd in cc make lex ; - do \ - which $cmd >/tmp/runltp.err.$$ 2>&1 ; - if [ $? -ne 0 ] ; - then \ - display_info_msg "Compiling LTP testsuite" \ - "ERROR: command $cmd not found, $cmd is\ - required to compile LTP test cases. Please\ - install $cmd or export PATH correctly before\ - running this program" ; - return ; - fi ; - done ; - make clean; - if [ $? -ne 0 ];then - echo "ERROR in \'make clean\' - exiting." - exit - fi - make ; - if [ $? -ne 0 ];then - echo "ERROR in \'make all\' - exiting." - exit - fi - make install ; - if [ $? -ne 0 ];then - echo "ERROR in \'make install\' - exiting." - exit - fi - return ;; - - 1) return ;; - - 255) return ;; - esac -} - - -# Function: disp_ltpres -# -# Description: The results generated after the ltp execution located under -# ltp-mmddyy/results/ directory in a text (ASCII) file called -# results.todaysdate. This function displays this file in a -# window. If the results file does not exit it displays an -# info message window notifing the user that LTP test cases -# need to be executed inorder to view results. -# -# Input: ltp-mmddyy/results/results.todaysdate.time -# -# Output: Window displaying results of testcases that were executed. -disp_ltpres() -{ - RC=0 - - RESULTS_LIST=$(for i in `ls -1 -A -I "CVS" results`;do echo -n "$i [more...] "; done) - if ! [ -z $RESULTS_LIST ] ;then - while [ $RC -ne "1" ] - do - dialog --clear - dialog --backtitle "Linux Test Project Control Centre" \ - --title "LTP Test Results" \ - --menu "Move using[UP] [DOWN], Select using [ENTER]" 15 70 8 \ - $RESULTS_LIST \ - 2>/tmp/runltp.results.$$ || RC=$? - results_item=$(cat /tmp/runltp.results.$$) - if ! [ -z $results_item ];then - dialog --clear - dialog --backtitle "Linux Test Project Control Centre" \ - --title "LTP Test Results" \ - --textbox results/$results_item 17 70 - - dialog --backtitle "Linux Test Project Control Centre" \ - --title "LTP Test Results." \ - --yesno "Would you like to share these results with the LTP \ - community by posting it to the LTP results mailing list?" \ - 7 70 || RESPONSE=$? - case $RESPONSE in - 0) \ - mail ltp-results@lists.sourceforge.net < \ - ./results/$results_item ; - ;; - - 1) ;; - - 255) ;; - esac - fi - done - else - dialog --clear - dialog --backtitle "Linux Test Project Control Centre" \ - --title "LTP Test Results" \ - --msgbox "ERROR: No files to view in /results directory." 5 53 - fi - return -} - - -# Function: flags_prompt -# -# Description: Prompt for and record user options for run duration and -# test output direction -# -# Input: none -# -# Output: none -flags_prompt() -{ - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Output Direction" --clear\ - --yesno "Would you like test output recorded to a file, instead of STDOUT?" 7 80 - RC=$? - if [ $RC -eq "0" ] - then - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Output Direction" --clear\ - --inputbox " Please enter the full path and \ - name of the file where you wish \ - to redirect output to" 17 80 \ - 2>/tmp/runltp.outdir.$$ ; - flags_outfile=$(cat /tmp/runltp.outdir.$$ | awk '{print $1}') - ./ver_linux > $flags_outfile 2>&1 - RUNALL_FLAGS=" -o $flags_outfile" - fi - - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Test Duration" --clear\ - --yesno "Would you like to specify test duration? \ - Default is the length of one loop." 7 80 - RC=$? - if [ $RC -eq "0" ] - then - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Test Duration - Interval Selection" --clear\ - --menu "Move using[UP] [DOWN], Select using [ENTER]" 15 70 4 \ - s "Seconds" \ - m "Minutes" \ - h "Hours" \ - d "Days" \ - 2>/tmp/runltp.interval.$$ ; - flags_interval=$(cat /tmp/runltp.interval.$$ | awk '{print $1}') - case $flags_interval in - s) INTERVAL="seconds" ;; - m) INTERVAL="minutes" ;; - h) INTERVAL="hours" ;; - d) INTERVAL="days" ;; - esac - - echo $INTERVAL - WINDOW_MSG="Please enter the number of $INTERVAL to run" - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Test Duration - Length Specification" --clear\ - --inputbox "$WINDOW_MSG" 7 80 \ - 2>/tmp/runltp.length.$$ ; - flags_length=$(cat /tmp/runltp.length.$$ | awk '{print $1}') - flags_duration="$flags_length$flags_interval" - RUNALL_FLAGS=" $RUNALL_FLAGS -t $flags_duration" - fi -} - -# Function: exectest_screenout -# -# Description: Execute tests by calling runltp, display test status -# in a window. -# -# Input: none -# -# Output: messages printed by testcases. -exectest_screenout() -{ - RC=0 # setting return code to 0, to loop in while - - RESULTS_FILE=$(date +%Y-%m-%d.%H.%M.%S).$$ - - # execute runltp with user defined command file. - ./runltp -q -p $RUNALL_FLAGS -l results.$RESULTS_FILE \ - -f /tmp/runltp.test.list.$$ - - sleep 2 - - return -} - - -# Function: execute_ltp -# -# Description: This function provides a menu of testcases that can be -# selected for execution. If networking tests are selected, -# they require a remote machine and remote machines root -# users password. The user will be prompted to enter this -# information in a text box. -# The function checks to see if the ltp-mmddyy/testcases/bin -# directory was created, this directory is created when the -# testcases are compiled and installed, if it is not found -# an info message window will notify the user that LTP needs to -# be compiled before tests can be executed. -# This function creates the senatrio file based on the users -# choice of testcases and uses the runltp script to -# execute these tests. -# The messages printed by the testcases are displayed on this -# terminal. -# -# Input: Users selection of testcases; scenario file. -# -# Output: Test selection window, Message window, -# information message window -execute_ltp() -{ - RC=0 - host_name=" " - rhost_passwd=" " - run_net_test=" " - - if ! [ -d ./testcases/bin ] - then - display_info_msg "Executing LTP testcases" \ - "The testcases must to be compiled inorder\ - to execute them. Returning to main menu. \ - Please select the Compile option." - return - fi - - LIST=$(for i in `ls -1 -A -I "CVS" runtest`; do echo -n "$i "; j=$(head -n1 runtest/$i | cut -d: -f2|sed s/" "/_/g); echo -n "$j off "; done) - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Execute LTP" --clear\ - --checklist "Select [SPACEBAR] tests to run" 20 80 5 \ - $LIST \ - 2>/tmp/runltp.choice.$$ || RC=$? - size=`wc -m /tmp/runltp.choice.$$|awk '{print $1}'` - if [ $size -eq 0 ];then - tst_choice=$(echo "NULL") - else - tst_choice=$(cat /tmp/runltp.choice.$$) - fi - if [[ $tst_choice == NULL ]];then - RC=1 - fi - case $RC in - 0) \ - for i in $tst_choice ; - do \ - cat ./runtest/$(echo $i | sed -e 's/"//g') \ - >> /tmp/runltp.test.list.$$ ; - if [[ $(echo $i | sed -e 's/"//g') == "tcp_cmds" || \ - $(echo $i | sed -e 's/"//g') == "tcp_cmds_noexpect" || \ - $(echo $i | sed -e 's/"//g') == "multicast" || \ - $(echo $i | sed -e 's/"//g') == "ipv6" || \ - $(echo $i | sed -e 's/"//g') == "ipv6_noexpect" || \ - $(echo $i | sed -e 's/"//g') == "nfs" || \ - $(echo $i | sed -e 's/"//g') == "multicast" ]] ; - then \ - run_net_test="Y" ; - fi ; - - done ; - if ! [ -z $run_net_test ] ; - then \ - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Execute LTP test cases" \ - --clear \ - --inputbox "You have chosen to execute testcases \ - that require a Remote Machine. \ - Please enter the fully qualified host \ - name" 17 80 $(hostname --long) \ - 2>/tmp/runltp.out.$$ ; - host_name=$(cat /tmp/runltp.out.$$ | awk '{print $1}') ; - unset $RHOST ; - RHOST=$host_name ; - export RHOST; - - dialog --backtitle "Linux Test Project Control Centre"\ - --title "Execute LTP test cases" \ - --clear \ - --inputbox " Please enter the root password \ - of this remote machine" 17 80 \ - 2>/tmp/runltp.out.$$ ; - rhost_passwd=$(cat /tmp/runltp.out.$$ | awk '{print $1}') ; - - PASSWD=$rhost_passwd ; - export PASSWD; - fi ; - - if ! [ -d ./testcases/bin ] ; - then \ - display_info_msg "Executing LTP testcases" \ - "The testcases must to be compiled inorder\ - to execute them. Returning to main menu. \ - Please select the Compile option." ; - return ; - fi ; - - dialog --clear ; - - flags_prompt ; - - exectest_screenout ; - - return ;; - 1) \ - # echo "Cancel pressed" ; - return ;; - 255) \ - # echo "ESC pressed" ; - return ;; - esac -} - - -# Function: about_ltpcc -# -# Description: This function displays a window containing a brief message -# describing this programs functionality, and credits the author. -# -# Input: NONE -# -# Output: Message window, description of LTP Control Center. -about_ltpcc() -{ - display_info_msg "About LTP Control Centre" \ - "The LTP Control Centre can be used to\ - to compile, install and execute\ - The Linux Test Project test suite. Written by\ - Manoj Iyer " - return -} - - -# Function: ltp_scenarios -# -# Description: This function displays a list of scenario files located -# in /runtest. Users can list the contents of each file. -# -# Input: Files from /runtest -# -# Output: 1) Menu selection containing each file as an option to list. -# 2) Contents of selected scenario. -ltp_scenarios() -{ - -RC=0 -SCENARIOS=$(for i in `ls -1 -A -I "CVS" runtest`;do echo -n "$i [more...] "; done) - -while [ $RC -ne "1" ] -do - dialog --clear - dialog --backtitle "Linux Test Project Control Centre" \ - --title "LTP Scenario Files" \ - --menu "Move using[UP] [DOWN], Select using [ENTER]" 15 70 8 \ - $SCENARIOS \ - 2>/tmp/runltp.scenario.$$ || RC=$? - scenario_item=$(cat /tmp/runltp.scenario.$$) - if ! [ -z $scenario_item ];then - dialog --clear - dialog --backtitle "Linux Test Project Control Centre" \ - --title "LTP Scenario Files" \ - --textbox runtest/$scenario_item 17 70 - fi -done -} - - - -# Function: main -# -# Description: Displays the main menu to the LTP Control Centre. The menu -# provides options to Compile, Execute, and View test execution -# results. -# -# Calls: about_ltpcc() -# compile_ltp() -# execute_ltp() -# disp_ltpres() -# -# Input: NONE -# -# Output: Menu selection of actions to perform. - -# Global variables. -RC=0 # return code from commands and local functions -mmenu_item=" " -RHOST=" " -PASSWD=" " -RUNALL_FLAGS=" " -RESULTS_FILE=" " - -# test for dialog program exist -if [ ! -x /usr/bin/dialog ]; then - echo "Sorry, ltpmenu GUI not available, can't find dialog. Exiting..."; - exit 1; -fi - -# call cleanup function on program exit. -trap "cleanup" 0 - - -# wait in a loop until user hits [Cancel] button on the main menu. -while : -do - RC=0 - dialog --clear - dialog --backtitle "Linux Test Project Control Centre" \ - --title "Main Menu" \ - --menu "Move using[UP] [DOWN], Select using [ENTER]" 15 70 5 \ - About "About LTP Control Centre" \ - Compile "Compile LTP testsuite" \ - Details "Details of scenario files" \ - Execute "Execute LTP testsuite" \ - Results "Display a summary of test results" \ - 2>/tmp/runltp.mainmenu.$$ || RC=$? - - case $RC in - 0) mmenu_item=`cat /tmp/runltp.mainmenu.$$` ; - # echo "return code = $RC" ; - # echo "MENU ITEM = $mmenu_item" ; - case $mmenu_item in - About) about_ltpcc ;; - Compile) compile_ltp ;; - Details) ltp_scenarios ;; - Execute) execute_ltp ;; - Results) disp_ltpres ;; - esac ;; - - 1) display_info_msg "Good Bye!" \ - "Thank you for using Linux Test Project test suite.\ - Please visit our project website \ - http://ltp.sourceforge.net \ - for latest news on The Linux Test Project. " - exit ;; - - 255) display_info_msg "Good Bye!" \ - "Thank you for using Linux Test Project test suite.\ - Please visit our project website\ - http://ltp.sourceforge.net for latest news\ - on The Linux Test Project. " - exit;; - esac -done diff --git a/m4/ax_prog_perl_modules.m4 b/m4/ax_prog_perl_modules.m4 deleted file mode 100755 index 70b3230e..00000000 --- a/m4/ax_prog_perl_modules.m4 +++ /dev/null @@ -1,77 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_prog_perl_modules.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PROG_PERL_MODULES([MODULES], [ACTION-IF-TRUE], [ACTION-IF-FALSE]) -# -# DESCRIPTION -# -# Checks to see if the given perl modules are available. If true the shell -# commands in ACTION-IF-TRUE are executed. If not the shell commands in -# ACTION-IF-FALSE are run. Note if $PERL is not set (for example by -# calling AC_CHECK_PROG, or AC_PATH_PROG), AC_CHECK_PROG(PERL, perl, perl) -# will be run. -# -# MODULES is a space separated list of module names. To check for a -# minimum version of a module, append the version number to the module -# name, separated by an equals sign. -# -# Example: -# -# AX_PROG_PERL_MODULES( Text::Wrap Net::LDAP=1.0.3, , -# AC_MSG_WARN(Need some Perl modules) -# -# LICENSE -# -# Copyright (c) 2009 Dean Povey -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 8 - -AU_ALIAS([AC_PROG_PERL_MODULES], [AX_PROG_PERL_MODULES]) -AC_DEFUN([AX_PROG_PERL_MODULES],[dnl - -m4_define([ax_perl_modules]) -m4_foreach([ax_perl_module], m4_split(m4_normalize([$1])), - [ - m4_append([ax_perl_modules], - [']m4_bpatsubst(ax_perl_module,=,[ ])[' ]) - ]) - -# Make sure we have perl -if test -z "$PERL"; then -AC_CHECK_PROG(PERL,perl,perl) -fi - -if test "x$PERL" != x; then - ax_perl_modules_failed=0 - for ax_perl_module in ax_perl_modules; do - AC_MSG_CHECKING(for perl module $ax_perl_module) - - # Would be nice to log result here, but can't rely on autoconf internals - $PERL -e "use $ax_perl_module; exit" > /dev/null 2>&1 - if test $? -ne 0; then - AC_MSG_RESULT(no); - ax_perl_modules_failed=1 - else - AC_MSG_RESULT(ok); - fi - done - - # Run optional shell commands - if test "$ax_perl_modules_failed" = 0; then - : - $2 - else - : - $3 - fi -else - AC_MSG_WARN(could not find perl) -fi])dnl diff --git a/m4/ltp-cap.m4 b/m4/ltp-cap.m4 index 4981e843..6b26e7d1 100755 --- a/m4/ltp-cap.m4 +++ b/m4/ltp-cap.m4 @@ -1,6 +1,6 @@ dnl SPDX-License-Identifier: GPL-2.0-or-later dnl Copyright (c) Cisco Systems Inc., 2009 -dnl Copyright (c) Linux Test Project, 2019 +dnl Copyright (c) Linux Test Project, 2019-2025 dnl Author: Ngie Cooper AC_DEFUN([LTP_CHECK_CAPABILITY_SUPPORT],[ @@ -14,19 +14,4 @@ if test "x$cap_libs" != x; then AC_DEFINE(HAVE_LIBCAP) fi AC_SUBST(CAP_LIBS,$cap_libs) - -AH_TEMPLATE(HAVE_NEWER_LIBCAP, -[Define to 1 if you have newer libcap-2 installed.]) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([ -#include -#include -int main(void) { - __u16 a; - __u32 b; - return 0; -}])],[has_newer_libcap="yes"]) - -if test "x$has_newer_libcap" = xyes; then - AC_DEFINE(HAVE_NEWER_LIBCAP) -fi ]) diff --git a/m4/ltp-docparse.m4 b/m4/ltp-docparse.m4 deleted file mode 100755 index 9514e5e1..00000000 --- a/m4/ltp-docparse.m4 +++ /dev/null @@ -1,118 +0,0 @@ -dnl SPDX-License-Identifier: GPL-2.0-or-later -dnl Copyright (c) 2020 Petr Vorel - -AC_DEFUN([LTP_CHECK_METADATA_GENERATOR_ASCIIDOCTOR], [ - AC_MSG_NOTICE(checking asciidoctor as metadata generator) - AC_PATH_TOOL(asciidoctor, "asciidoctor") - metadata_generator_html=$asciidoctor - # pdf requires both asciidoctor and asciidoctor-pdf - if test "x$metadata_generator_html" != x; then - AC_PATH_TOOL(asciidoctor_pdf, "asciidoctor-pdf") - metadata_generator_pdf=$asciidoctor_pdf - fi -]) - -AC_DEFUN([LTP_CHECK_METADATA_GENERATOR_ASCIIDOC], [ - AC_MSG_NOTICE(checking asciidoc as metadata generator) - AC_PATH_TOOL(a2x, "a2x") - if test "x$a2x" != x; then - version="`$a2x --version | cut -d ' ' -f2 `" - AX_COMPARE_VERSION([$version], lt, 9, [ - AC_MSG_WARN([a2x unsupported version: $version. Use a2x >= 9]) - a2x= - ]) - fi - metadata_generator_html=$a2x - # pdf requires both asciidoc and dblatex - if test "x$metadata_generator_html" != x; then - AC_PATH_TOOL(dblatex, "dblatex") - metadata_generator_pdf=$dblatex - fi -]) - -AC_DEFUN([LTP_DOCPARSE], [ -with_metadata=no -with_metadata_html=no -with_metadata_pdf=no - -if test "x$enable_metadata" != xyes; then - enable_metadata_html=no - enable_metadata_pdf=no - with_metadata_generator=none -fi - -if test "x$enable_metadata_html" = xyes -o "x$enable_metadata_pdf" = xyes; then - AX_PROG_PERL_MODULES(Cwd File::Basename JSON LWP::Simple) -fi - -if test "x$ax_perl_modules_failed" = x0; then - if test "x$with_metadata_generator" = xasciidoctor -o "x$with_metadata_generator" = xdetect; then - LTP_CHECK_METADATA_GENERATOR_ASCIIDOCTOR - elif test "x$with_metadata_generator" = xasciidoc; then - LTP_CHECK_METADATA_GENERATOR_ASCIIDOC - else - AC_MSG_ERROR([invalid metadata generator '$with_metadata_generator', use --with-metadata-generator=asciidoc|asciidoctor]) - fi - - # autodetection: check also Asciidoc - if test "x$with_metadata_generator" = xdetect; then - with_metadata_generator='asciidoctor' - # problems with Asciidoctor: (html enabled && not found) || (pdf enabled && not found) => try Asciidoc - if test "x$enable_metadata_html" = xyes -a "x$metadata_generator_html" = x || - test "x$enable_metadata_pdf" = xyes -a "x$metadata_generator_pdf" = x; then - backup_html="$metadata_generator_html" - backup_pdf="$metadata_generator_pdf" - AC_MSG_NOTICE(missing some dependencies for Asciidoctor => trying Asciidoc) - with_metadata_generator='asciidoc' - LTP_CHECK_METADATA_GENERATOR_ASCIIDOC - # prefer Asciidoctor if it's not worse than Asciidoc - # (html not enabled || asciidoctor html found || asciidoc html not found) && (pdf ...) - if test "x$enable_metadata_html" != xyes -o "x$backup_html" != x -o "x$metadata_generator_html" = x && - test "x$enable_metadata_pdf" != xyes -o "x$backup_pdf" != x -o "x$metadata_generator_pdf" = x; then - with_metadata_generator='asciidoctor' - metadata_generator_html="$backup_html" - metadata_generator_pdf="$backup_pdf" - fi - fi - if test "x$metadata_generator_html" != x -o "x$metadata_generator_pdf" != x; then - AC_MSG_NOTICE(choosing $with_metadata_generator for metadata generation) - fi - fi - - if test "x$enable_metadata_html" = xno; then - AC_MSG_NOTICE(HTML metadata generation disabled) - elif test "x$metadata_generator_html" != x; then - with_metadata_html=yes - fi - - if test "x$enable_metadata_pdf" = xno; then - AC_MSG_NOTICE(PDF metadata generation disabled) - elif test "x$metadata_generator_pdf" != x; then - with_metadata_pdf=yes - fi -fi - -reason="metadata generation skipped due missing suitable generator" -hint="specify correct generator with --with-metadata-generator=asciidoc|asciidoctor or use --disable-metadata|--disable-metadata-html|--disable-metadata-pdf" - -if test -z "$ax_perl_modules_failed"; then - AC_MSG_NOTICE(metadata generation disabled) -elif test "x$ax_perl_modules_failed" = x1; then - AC_MSG_WARN(metadata generation skipped due missing required Perl modules) -elif test "x$with_metadata_html" = xno -a "x$with_metadata_pdf" = xno; then - AC_MSG_WARN([$reason, $hint]) -else - with_metadata=yes - AC_SUBST(METADATA_GENERATOR, $with_metadata_generator) - if test "x$with_metadata_html" = xno -a "x$enable_metadata_html" = xyes; then - AC_MSG_WARN([HTML $reason, $hint]) - fi - if test "x$with_metadata_pdf" = xno -a "x$enable_metadata_pdf" = xyes; then - AC_MSG_WARN([PDF $reason, $hint]) - fi -fi - -AC_SUBST(WITH_METADATA, $with_metadata) -AC_SUBST(WITH_METADATA_HTML, $with_metadata_html) -AC_SUBST(WITH_METADATA_PDF, $with_metadata_pdf) -]) diff --git a/m4/ltp-fcntl.m4 b/m4/ltp-fcntl.m4 deleted file mode 100755 index 90ab5fd0..00000000 --- a/m4/ltp-fcntl.m4 +++ /dev/null @@ -1,21 +0,0 @@ -dnl SPDX-License-Identifier: GPL-2.0-or-later -dnl Copyright (c) 2014 Fujitsu Ltd. -dnl Author: Xiaoguang Wang - -AC_DEFUN([LTP_CHECK_SYSCALL_FCNTL],[ - AC_MSG_CHECKING([for fcntl f_owner_ex]) - AC_COMPILE_IFELSE([AC_LANG_SOURCE([ -#define _GNU_SOURCE -#include -int main(void) { - struct f_owner_ex tst_own_ex; - return 0; -}])],[has_f_owner_ex="yes"]) - -if test "x$has_f_owner_ex" = xyes; then - AC_DEFINE(HAVE_STRUCT_F_OWNER_EX,1,[Define to 1 if you have struct f_owner_ex]) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi -]) diff --git a/m4/ltp-linuxrandom.m4 b/m4/ltp-linuxrandom.m4 deleted file mode 100755 index 4f6b9d13..00000000 --- a/m4/ltp-linuxrandom.m4 +++ /dev/null @@ -1,19 +0,0 @@ -dnl SPDX-License-Identifier: GPL-2.0-or-later -dnl Copyright (c) Linux Test Project, 2015 - -AC_DEFUN([LTP_CHECK_LINUXRANDOM],[ - AC_MSG_CHECKING([for linux/random.h]) - AC_COMPILE_IFELSE([AC_LANG_SOURCE([ - -#include -int main(void) { - return 0; -}])],[has_linux_random_h="yes"]) - -if test "x$has_linux_random_h" = xyes; then - AC_DEFINE(HAVE_LINUX_RANDOM_H,1,[Define to 1 if having a valid linux/random.h]) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi -]) diff --git a/m4/ltp-nommu-linux.m4 b/m4/ltp-nommu-linux.m4 deleted file mode 100755 index 7471ddd0..00000000 --- a/m4/ltp-nommu-linux.m4 +++ /dev/null @@ -1,14 +0,0 @@ -dnl SPDX-License-Identifier: GPL-2.0-or-later -dnl Copyright (c) Linux Test Project, 2010 -dnl Author: Mike Frysinger - -AC_DEFUN([LTP_CHECK_NOMMU_LINUX], -[ - AC_CHECK_FUNCS([fork daemon vfork]) - UCLINUX=0 - if test "x$ac_cv_func_fork" = "xno" ; then - UCLINUX=1 - AC_DEFINE([UCLINUX], 1, [Target is running Linux w/out an MMU]) - fi - AC_SUBST(UCLINUX) -]) diff --git a/m4/ltp-ptrace.m4 b/m4/ltp-ptrace.m4 deleted file mode 100755 index 6f03fa57..00000000 --- a/m4/ltp-ptrace.m4 +++ /dev/null @@ -1,26 +0,0 @@ -dnl SPDX-License-Identifier: GPL-2.0-or-later -dnl Copyright (c) Jiri Palecek 2009 - -AC_DEFUN([LTP_CHECK_LINUX_PTRACE], -_LTP_CHECK_LINUX_PTRACE -) - -dnl Check for ptrace support -dnl in commit 016ae219 in July 2008 -AC_DEFUN([_LTP_CHECK_LINUX_PTRACE],[ -dnl order of headers checked here is significant -AC_CHECK_HEADERS([ \ - sys/ptrace.h \ - sys/reg.h \ - asm/ptrace.h \ - linux/ptrace.h \ -]) -save_CPPFLAGS=$CPPFLAGS -CPPFLAGS="$CPPFLAGS -I$srcdir/testcases/kernel/syscalls/ptrace" -AC_CHECK_TYPES([struct user_regs_struct, struct pt_regs],,,[#include "ptrace.h"]) -AC_CHECK_DECLS([PTRACE_GETSIGINFO, PTRACE_O_TRACEVFORKDONE, PTRACE_SETOPTIONS],,,[#include "ptrace.h"]) -dnl glibc-2.18 defines ptrace_peeksiginfo_args in sys/ptrace.h which -dnl conflicts with the one from linux kernel in linux/ptrace.h -AC_CHECK_TYPES([struct ptrace_peeksiginfo_args],,,[#include ]) -CPPFLAGS=$save_CPPFLAGS -]) diff --git a/m4/ltp-signalfd.m4 b/m4/ltp-signalfd.m4 deleted file mode 100755 index 5aac395b..00000000 --- a/m4/ltp-signalfd.m4 +++ /dev/null @@ -1,17 +0,0 @@ -dnl SPDX-License-Identifier: GPL-2.0-or-later -dnl Copyright (c) Red Hat Inc., 2008 -dnl Copyright (c) 2019 Fujitsu Ltd. -dnl Author: Masatake YAMATO - -AC_DEFUN([LTP_CHECK_SYSCALL_SIGNALFD],[ - -AC_CHECK_FUNCS(signalfd,,) -AC_CHECK_HEADERS([sys/signalfd.h],,) -AC_CHECK_HEADERS([linux/signalfd.h],,) -AC_CHECK_MEMBERS([struct signalfd_siginfo.ssi_signo],,,[ -#if defined HAVE_SYS_SIGNALFD_H -#include -#elif defined HAVE_LINUX_SIGNALFD_H -#include -#endif]) -]) diff --git a/metadata/.gitignore b/metadata/.gitignore index 07d2fd6f..bb6399e5 100755 --- a/metadata/.gitignore +++ b/metadata/.gitignore @@ -1,2 +1,3 @@ metaparse +metaparse-sh ltp.json diff --git a/metadata/Makefile b/metadata/Makefile index 522af427..6939b9f7 100755 --- a/metadata/Makefile +++ b/metadata/Makefile @@ -7,22 +7,13 @@ include $(top_srcdir)/include/mk/env_pre.mk include $(top_srcdir)/include/mk/functions.mk MAKE_TARGETS := ltp.json -HOST_MAKE_TARGETS := metaparse +HOST_MAKE_TARGETS := metaparse metaparse-sh INSTALL_DIR = metadata .PHONY: ltp.json -ltp.json: metaparse +ltp.json: metaparse metaparse-sh $(abs_srcdir)/parse.sh > ltp.json -ifeq ($(WITH_METADATA),yes) - mkdir -p $(abs_top_builddir)/docparse - $(MAKE) -C $(abs_top_builddir)/docparse/ -f $(abs_top_srcdir)/docparse/Makefile -endif - -ifeq ($(WITH_METADATA),yes) -install: - $(MAKE) -C $(abs_top_builddir)/docparse/ -f $(abs_top_srcdir)/docparse/Makefile install -endif test: $(MAKE) -C $(abs_srcdir)/tests/ test diff --git a/docparse/README.md b/metadata/README.md old mode 100755 new mode 100644 similarity index 99% rename from docparse/README.md rename to metadata/README.md index 9ccfa44a..c7106275 --- a/docparse/README.md +++ b/metadata/README.md @@ -234,7 +234,7 @@ with a header describing the testsuite: Open Points =========== -There are stil some loose ends. Mostly it's not well defined where to put +There are still some loose ends. Mostly it's not well defined where to put things and how to format them. * Some of the hardware requirements are already listed in the tst\_test. Should diff --git a/metadata/data_storage.h b/metadata/data_storage.h index 91ea70a0..6ca5d7d9 100755 --- a/metadata/data_storage.h +++ b/metadata/data_storage.h @@ -10,12 +10,14 @@ #include #include #include +#include enum data_type { DATA_ARRAY, DATA_HASH, DATA_STRING, DATA_INT, + DATA_NULL, }; struct data_node_array { @@ -68,6 +70,8 @@ static inline const char* data_type_name(enum data_type type) return "string"; case DATA_INT: return "int"; + case DATA_NULL: + return "null"; default: return "???"; } @@ -100,6 +104,18 @@ static inline struct data_node *data_node_int(long i) return node; } +static inline struct data_node *data_node_null(void) +{ + struct data_node *node = malloc(sizeof(struct data_node)); + + if (!node) + return NULL; + + node->type = DATA_NULL; + + return node; +} + #define MAX_ELEMS 100 static inline struct data_node *data_node_hash(void) @@ -159,6 +175,7 @@ static inline void data_node_free(struct data_node *self) switch (self->type) { case DATA_STRING: case DATA_INT: + case DATA_NULL: break; case DATA_HASH: for (i = 0; i < self->hash.elems_used; i++) { @@ -196,7 +213,7 @@ static inline int data_node_hash_del(struct data_node *self, const char *id) return 1; } -static struct data_node *data_node_hash_get(struct data_node *self, const char *id) +static inline struct data_node *data_node_hash_get(struct data_node *self, const char *id) { unsigned int i; struct data_node_hash *hash = &self->hash; @@ -212,6 +229,13 @@ static struct data_node *data_node_hash_get(struct data_node *self, const char * return hash->elems[i].node; } +static inline unsigned int data_node_hash_len(struct data_node *self) +{ + struct data_node_hash *hash = &self->hash; + + return hash->elems_used; +} + static inline int data_node_array_add(struct data_node *self, struct data_node *payload) { if (self->type != DATA_ARRAY) @@ -235,12 +259,52 @@ static inline unsigned int data_node_array_len(struct data_node *self) return self->array.array_used; } + +static inline struct data_node *data_node_array_last(struct data_node *self) +{ + if (self->type != DATA_ARRAY) + return NULL; + + unsigned int array_used = self->array.array_used; + if (!array_used) + return NULL; + + return self->array.array[array_used-1]; +} + +static inline void data_node_array_last_rem(struct data_node *self) +{ + if (self->type != DATA_ARRAY) + return; + + unsigned int array_used = self->array.array_used; + if (!array_used) + return; + + data_node_free(self->array.array[array_used-1]); + + self->array.array[array_used-1] = NULL; + self->array.array_used--; +} + static inline void data_print_padd(unsigned int i) { while (i-- > 0) putchar(' '); } +static inline bool data_node_is_empty(struct data_node *self) +{ + switch (self->type) { + case DATA_ARRAY: + return data_node_array_len(self) == 0; + case DATA_HASH: + return data_node_hash_len(self) == 0; + default: + return false; + } +} + static inline void data_node_print_(struct data_node *self, unsigned int padd) { unsigned int i; @@ -254,6 +318,10 @@ static inline void data_node_print_(struct data_node *self, unsigned int padd) data_print_padd(padd); printf("'%s'\n", self->string.val); break; + case DATA_NULL: + data_print_padd(padd); + printf("null\n"); + break; case DATA_HASH: for (i = 0; i < self->hash.elems_used; i++) { data_print_padd(padd); @@ -297,7 +365,6 @@ static inline void data_fprintf(FILE *f, unsigned int padd, const char *fmt, ... va_end(va); } - static inline void data_fprintf_esc(FILE *f, unsigned int padd, const char *str) { while (padd-- > 0) @@ -344,15 +411,21 @@ static inline void data_to_json_(struct data_node *self, FILE *f, unsigned int p padd = do_padd ? padd : 0; data_fprintf_esc(f, padd, self->string.val); break; + case DATA_NULL: + padd = do_padd ? padd : 0; + data_fprintf(f, padd, "null"); + break; case DATA_HASH: + data_fprintf(f, do_padd ? padd : 0, "{\n"); for (i = 0; i < self->hash.elems_used; i++) { - data_fprintf(f, padd, "\"%s\": ", self->hash.elems[i].id); + data_fprintf(f, padd+1, "\"%s\": ", self->hash.elems[i].id); data_to_json_(self->hash.elems[i].node, f, padd+1, 0); if (i < self->hash.elems_used - 1) fprintf(f, ",\n"); else fprintf(f, "\n"); } + data_fprintf(f, padd, "}"); break; case DATA_ARRAY: data_fprintf(f, do_padd ? padd : 0, "[\n"); @@ -370,9 +443,7 @@ static inline void data_to_json_(struct data_node *self, FILE *f, unsigned int p static inline void data_to_json(struct data_node *self, FILE *f, unsigned int padd) { - fprintf(f, "{\n"); - data_to_json_(self, f, padd + 1, 1); - data_fprintf(f, padd, "}"); + data_to_json_(self, f, padd, 0); } #endif /* DATA_STORAGE_H__ */ diff --git a/metadata/metaparse-sh.c b/metadata/metaparse-sh.c new file mode 100644 index 00000000..d9b30b98 --- /dev/null +++ b/metadata/metaparse-sh.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Cyril Hrubis + */ + +#include +#include +#include +#include + +#include "data_storage.h" + +static int started; + +static void json_start(char *path) +{ + if (started) + return; + + started = 1; + + printf(" \"%s\": {\n", basename(path)); +} + +static void json_finish(const char *path) +{ + if (!started) + return; + + printf(" \"fname\": \"%s\"\n", path); + printf(" }"); +} + +enum state { + NONE, + START, + DOC_FIRST, + DOC, + ENV_START, + ENV_FIRST, + ENV +}; + +static void parse_shell(char *path) +{ + char line[4096]; + FILE *f = fopen(path, "r"); + enum state state = NONE; + + if (!f) { + fprintf(stderr, "Failed to open '%s': %s\n", + path, strerror(errno)); + exit(1); + } + + while (fgets(line, sizeof(line), f)) { + /* Strip newline */ + line[strlen(line)-1] = 0; + + switch (state) { + case NONE: + if (!strcmp(line, "# ---")) + state = START; + break; + case START: + if (!strcmp(line, "# doc")) { + json_start(path); + state = DOC_FIRST; + printf(" \"doc\": [\n"); + } else if (!strcmp(line, "# env")) { + json_start(path); + state = ENV_START; + } else { + state = NONE; + } + break; + case DOC: + case DOC_FIRST: + if (!strcmp(line, "# ---")) { + state = NONE; + printf("\n ],\n"); + continue; + } + + if (state == DOC_FIRST) + state = DOC; + else + printf(",\n"); + + data_fprintf_esc(stdout, 4, line+2); + break; + case ENV_START: + if (!strcmp(line, "# {")) { + state = ENV_FIRST; + } else { + fprintf(stderr, + "%s: Invalid line in JSON block '%s'", + path, line); + } + break; + case ENV: + case ENV_FIRST: + if (!strcmp(line, "# }")) { + state = NONE; + printf(",\n"); + continue; + } + + if (state == ENV_FIRST) + state = ENV; + else + printf("\n"); + + line[0] = ' '; + line[1] = ' '; + + printf("%s", line); + break; + } + } + + json_finish(path); +} + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) + parse_shell(argv[i]); + + return 0; +} diff --git a/metadata/metaparse.c b/metadata/metaparse.c index 2384c73c..36736ac0 100755 --- a/metadata/metaparse.c +++ b/metadata/metaparse.c @@ -25,7 +25,7 @@ static char *includepath; #define WARN(str) fprintf(stderr, "WARNING: " str "\n") -static void oneline_comment(FILE *f) +static void remove_to_newline(FILE *f) { int c; @@ -126,7 +126,7 @@ static void maybe_comment(FILE *f, struct data_node *doc) switch (c) { case '/': - oneline_comment(f); + remove_to_newline(f); break; case '*': maybe_doc_comment(f, doc); @@ -173,6 +173,10 @@ static char *next_token2(FILE *f, char *buf, size_t buf_len, struct data_node *d case '[': case ']': case '#': + case '|': + case '+': + case '*': + case '%': if (i) { ungetc(c, f); goto exit; @@ -234,6 +238,20 @@ static FILE *open_file(const char *dir, const char *fname) return f; } +/** + * List of includes to be skipped. + * + * These define many macros or include many include files that are mostly + * useless to values expanded in tst_test structure. Or macros that shouldn't + * be expanded at all. + */ +static const char *skip_includes[] = { + "\"tst_test.h\"", + "\"config.h\"", + "\"tst_taint.h\"", + NULL +}; + static FILE *open_include(FILE *f) { char buf[256], *fname; @@ -246,6 +264,20 @@ static FILE *open_include(FILE *f) if (buf[0] != '"') return NULL; + for (i = 0; skip_includes[i]; i++) { + if (!strcmp(skip_includes[i], buf)) { + if (verbose) + fprintf(stderr, "INCLUDE SKIP %s\n", buf); + return NULL; + } + } + + if (!strncmp(buf, "\"lapi/", 6)) { + if (verbose) + fprintf(stderr, "INCLUDE SKIP %s\n", buf); + return NULL; + } + fname = buf + 1; if (!buf[0]) @@ -286,43 +318,6 @@ static void close_include(FILE *inc) fclose(inc); } -static int parse_array(FILE *f, struct data_node *node) -{ - const char *token; - - for (;;) { - if (!(token = next_token(f, NULL))) - return 1; - - if (!strcmp(token, "{")) { - struct data_node *ret = data_node_array(); - parse_array(f, ret); - - if (data_node_array_len(ret)) - data_node_array_add(node, ret); - else - data_node_free(ret); - - continue; - } - - if (!strcmp(token, "}")) - return 0; - - if (!strcmp(token, ",")) - continue; - - if (!strcmp(token, "NULL")) - continue; - - struct data_node *str = data_node_string(token); - - data_node_array_add(node, str); - } - - return 0; -} - static void try_apply_macro(char **res) { ENTRY macro = { @@ -342,6 +337,193 @@ static void try_apply_macro(char **res) *res = ret->data; } +static void finalize_array_entry(char **val, char **id, struct data_node *node) +{ + if (!*val) + return; + + if (*id) + data_node_hash_add(node, *id+1, data_node_string(*val)); + else + data_node_array_add(node, data_node_string(*val)); + + free(*id); + free(*val); + *id = NULL; + *val = NULL; +} + +static void str_append(char **res, char *append) +{ + char *cur_str = *res; + + if (!cur_str) { + *res = strdup(append); + if (!*res) + goto err; + return; + } + + if (asprintf(res, "%s%s", cur_str, append) < 0) + goto err; + + free(cur_str); + return; +err: + fprintf(stderr, "Allocation failed :(\n"); + exit(1); +} + +static int array_is_hash(FILE *f) +{ + long pos = ftell(f); + int has_ids = 1; + int elem_seen = 0; + int comma_last = 0; + int in_id = 1; + char *token; + + while ((token = next_token(f, NULL))) { + + if (!strcmp(token, "}")) { + if (in_id && !comma_last) + has_ids = 0; + goto ret; + } + + elem_seen = 1; + + if (!strcmp(token, "{")) { + if (in_id) { + has_ids = 0; + goto ret; + } + + int level = 1; + + for (;;) { + token = next_token(f, NULL); + + if (!token) + goto ret; + + if (!strcmp(token, "{")) + level++; + + if (!strcmp(token, "}")) + level--; + + if (!level) + break; + } + } else if (!strcmp(token, ",")) { + if (in_id) { + has_ids = 0; + goto ret; + } + + in_id = 1; + + comma_last = 1; + } else if (!strcmp(token, "=")) { + in_id = 0; + } else { + comma_last = 0; + } + } + +ret: + fseek(f, pos, SEEK_SET); + return elem_seen && has_ids; +} + +static int parse_array(FILE *f, const char *arr_id, struct data_node **ret) +{ + char *token; + char *val = NULL, *id = NULL; + int parent_cnt = 0; + int is_hash = array_is_hash(f); + + if (verbose) + fprintf(stderr, "PARSING ARRAY (%s) is_hash = %i\n", arr_id, is_hash); + + if (is_hash) + *ret = data_node_hash(); + else + *ret = data_node_array(); + + for (;;) { + if (!(token = next_token(f, NULL))) + return 1; + + if (!strcmp(token, "{")) { + struct data_node *sub_ret; + + parse_array(f, id, &sub_ret); + + if (data_node_is_empty(sub_ret)) { + data_node_free(sub_ret); + } else { + if (is_hash) + data_node_hash_add(*ret, id+1, sub_ret); + else + data_node_array_add(*ret, sub_ret); + } + + free(id); + id = NULL; + free(val); + val = NULL; + + continue; + } + + if (!strcmp(token, "}")) { + struct data_node *arr_last; + + finalize_array_entry(&val, &id, *ret); + /* Remove NULL terminating entry, if present. */ + if (!is_hash) { + arr_last = data_node_array_last(*ret); + if (arr_last && arr_last->type == DATA_NULL) + data_node_array_last_rem(*ret); + } + + return 0; + } + + if (is_hash && !strcmp(token, "=") && !id) { + id = val; + val = NULL; + continue; + } + + if (!strcmp(token, ",") && parent_cnt <= 0) { + finalize_array_entry(&val, &id, *ret); + continue; + } + + if (!strcmp(token, "NULL")) { + if (is_hash) + data_node_hash_add(*ret, id, data_node_null()); + else + data_node_array_add(*ret, data_node_null()); + continue; + } + + if (!strcmp(token, "(")) + parent_cnt++; + + if (!strcmp(token, ")")) + parent_cnt--; + + try_apply_macro(&token); + str_append(&val, token); + } + + return 0; +} + static int parse_get_array_len(FILE *f) { const char *token; @@ -486,6 +668,11 @@ static int parse_test_struct(FILE *f, struct data_node *doc, struct data_node *n if (!strcmp(token, "}")) return 0; + if (!strcmp(token, "#")) { + remove_to_newline(f); + continue; + } + switch (state) { case 0: id = strdup(token); @@ -515,8 +702,7 @@ static int parse_test_struct(FILE *f, struct data_node *doc, struct data_node *n } if (!strcmp(token, "{")) { - ret = data_node_array(); - parse_array(f, ret); + parse_array(f, id, &ret); } else if (!strcmp(token, "ARRAY_SIZE")) { if (parse_array_size(f, &ret)) return 1; @@ -638,12 +824,20 @@ static void parse_macro(FILE *f) hsearch(e, ENTER); } -static void parse_include_macros(FILE *f) +static void parse_include_macros(FILE *f, int level) { FILE *inc; const char *token; int hash = 0; + /** + * Allow only three levels of include indirection. + * + * Should be more than enough (TM). + */ + if (level >= 3) + return; + inc = open_include(f); if (!inc) return; @@ -659,6 +853,8 @@ static void parse_include_macros(FILE *f) if (!strcmp(token, "define")) parse_macro(inc); + else if (!strcmp(token, "include")) + parse_include_macros(inc, level+1); hash = 0; } @@ -666,6 +862,51 @@ static void parse_include_macros(FILE *f) close_include(inc); } +/* pre-defined macros that makes the output cleaner. */ +static const struct macro { + char *from; + char *to; +} internal_macros[] = { + {"TST_CG_V2", "2"}, + {"TST_CG_V1", "1"}, + {"TST_KB", "1024"}, + {"TST_MB", "1048576"}, + {"TST_GB", "1073741824"}, + {"TST_SR_TBROK", "TBROK"}, + {"TST_SR_TCONF", "TCONF"}, + {"TST_SR_SKIP", "SKIP"}, + {"TST_SR_TBROK_MISSING", "TBROK_MISSING"}, + {"TST_SR_TCONF_MISSING", "TCONF_MISSING"}, + {"TST_SR_SKIP_MISSING", "SKIP_MISSING"}, + {"TST_SR_TBROK_RO", "TBROK_RO"}, + {"TST_SR_TCONF_RO", "TCONF_RO"}, + {"TST_SR_SKIP_RO", "SKIP_RO"}, + {} +}; + +static void load_internal_macros(void) +{ + unsigned int i; + + if (verbose) + fprintf(stderr, "PREDEFINED MACROS\n"); + + for (i = 0; internal_macros[i].from; i++) { + ENTRY e = { + .key = internal_macros[i].from, + .data = internal_macros[i].to, + }; + + if (verbose) + fprintf(stderr, " MACRO %s=%s\n", e.key, (char*)e.data); + + hsearch(e, ENTER); + } + + if (verbose) + fprintf(stderr, "END PREDEFINED MACROS\n"); +} + static struct data_node *parse_file(const char *fname) { int state = 0, found = 0; @@ -683,6 +924,8 @@ static struct data_node *parse_file(const char *fname) struct data_node *res = data_node_hash(); struct data_node *doc = data_node_array(); + load_internal_macros(); + while ((token = next_token(f, doc))) { if (state < 6 && !strcmp(tokens[state], token)) { state++; @@ -694,7 +937,7 @@ static struct data_node *parse_file(const char *fname) parse_macro(f); if (!strcmp(token, "include")) - parse_include_macros(f); + parse_include_macros(f, 0); } } diff --git a/metadata/parse.sh b/metadata/parse.sh index 69bf5db6..45776e4d 100755 --- a/metadata/parse.sh +++ b/metadata/parse.sh @@ -30,7 +30,20 @@ echo ' "tests": {' first=1 for test in `find testcases/ -name '*.c'|sort`; do - a=$($top_builddir/metadata/metaparse -Iinclude -Itestcases/kernel/syscalls/utils/ "$test") + a=$($top_builddir/metadata/metaparse -Iinclude -Itestcases/kernel/syscalls/utils/ -Itestcases/kernel/include "$test") + if [ -n "$a" ]; then + if [ -z "$first" ]; then + echo ',' + fi + first= + cat <&2 + echo "INFO: runltp script is deprecated, try kirk" >&2 + echo "https://github.com/linux-test-project/kirk" >&2 + echo "-------------------------------------------" >&2 +} + setup() { + deprecated + cd `dirname $0` || \ { echo "FATAL: unable to change directory to $(dirname $0)" @@ -629,19 +639,6 @@ EOF } } - # The fsx-linux tests use the SCRATCHDEV environment variable as a location - # that can be reformatted and run on. Set SCRATCHDEV if you want to run - # these tests. As a safeguard, this is disabled. - unset SCRATCHDEV - [ -n "$SCRATCHDEV" ] && \ - { - cat ${LTPROOT}/runtest/fsx >> ${TMP}/alltests || - { - echo "FATAL: unable to create fsx-linux tests command file" - exit 1 - } - } - # If enabled, execute only test cases that match the PATTERN if [ -n "$TAG_RESTRICT_STRING" ] then @@ -934,6 +931,9 @@ EOF EOF } + + deprecated + exit $VALUE } diff --git a/runtest/Makefile b/runtest/Makefile index 6a1565b6..0f95e69f 100755 --- a/runtest/Makefile +++ b/runtest/Makefile @@ -32,10 +32,6 @@ UNWANTED_FILES := Makefile CVS STAX INSTALL_MODE := 00644 -ifneq ($(WITH_POWER_MANAGEMENT_TESTSUITE),yes) -UNWANTED_FILES += power_management_tests -endif - INSTALL_TARGETS := $(filter-out $(UNWANTED_FILES),$(notdir $(patsubst $(abs_srcdir)/%,%,$(sort $(wildcard $(abs_srcdir)/*))))) MAKE_TARGETS := diff --git a/runtest/cap_bounds b/runtest/cap_bounds deleted file mode 100755 index 518d1e3a..00000000 --- a/runtest/cap_bounds +++ /dev/null @@ -1,2 +0,0 @@ -#DESCRIPTION:Posix capability bounding set -Cap_bounds run_capbounds.sh diff --git a/runtest/securebits b/runtest/capability old mode 100755 new mode 100644 similarity index 53% rename from runtest/securebits rename to runtest/capability index 64e6e0a0..c7af1235 --- a/runtest/securebits +++ b/runtest/capability @@ -1,4 +1,7 @@ -#DESCRIPTION:securebits tests +# various capability related tests +cap_bounds run_capbounds.sh +filecaps filecapstest.sh + check_keepcaps01 check_keepcaps 1 check_keepcaps02 check_keepcaps 2 check_keepcaps03 check_keepcaps 3 diff --git a/runtest/commands b/runtest/commands index 5ec2c3b6..cfbaf214 100755 --- a/runtest/commands +++ b/runtest/commands @@ -5,7 +5,6 @@ ldd01_sh ldd01.sh nm01_sh nm01.sh file01_sh file01.sh tar01_sh tar_tests.sh -logrotate_sh export TCdat=$LTPROOT/testcases/bin; logrotate_tests.sh cpio01_sh cpio_tests.sh unzip01_sh unzip01.sh gzip01_sh gzip_tests.sh diff --git a/runtest/connectors b/runtest/connectors deleted file mode 100755 index 2c7aed47..00000000 --- a/runtest/connectors +++ /dev/null @@ -1,2 +0,0 @@ -#DESCRIPTION:Netlink Connector tests -cn_pec_sh cn_pec.sh diff --git a/runtest/crashme b/runtest/crashme index 47f5f93b..af45d29b 100755 --- a/runtest/crashme +++ b/runtest/crashme @@ -7,9 +7,6 @@ f00f f00f crash01 crash01 # Generate random code and execute it. Read f00f comment, # this test lockup SunOS,WindowsNT,etc. in seconds.. -crash02 crash02 -v 2 -# Generate random syscalls and execute them, less probability -# to hose your system, but still. -fork12 fork12 +crash02 crash02 # Fork as many children as possible. On systems with lots of memory # and kernels prior to 2.4.19, this can hang the system by using up all pids diff --git a/runtest/cve b/runtest/cve index f9b36a18..c3ecd74d 100755 --- a/runtest/cve +++ b/runtest/cve @@ -85,7 +85,11 @@ cve-2022-0847 dirtypipe cve-2022-2590 dirtyc0w_shmem cve-2022-23222 bpf_prog07 cve-2023-1829 tcindex01 +cve-2023-0461 setsockopt10 +cve-2023-31248 nft02 # Tests below may cause kernel memory leak cve-2020-25704 perf_event_open03 cve-2022-0185 fsconfig03 cve-2022-4378 cve-2022-4378 +cve-2025-38236 cve-2025-38236 +cve-2025-21756 cve-2025-21756 diff --git a/runtest/filecaps b/runtest/filecaps deleted file mode 100755 index 1872c0bc..00000000 --- a/runtest/filecaps +++ /dev/null @@ -1,2 +0,0 @@ -#DESCRIPTION:file capabilities -Filecaps filecapstest.sh diff --git a/runtest/fsx b/runtest/fsx deleted file mode 100755 index b09e5c2a..00000000 --- a/runtest/fsx +++ /dev/null @@ -1,8 +0,0 @@ -#DESCRIPTION:fsx filesystem stress tests -fsx-linux export TCbin=$LTPROOT/testcases/bin;fsxtest02 10000 -#fsx-ext2 fsxtest $SCRATCHDEV ext2 10000 -#fsx-ext3 fsxtest $SCRATCHDEV ext3 10000 -#fsx-jfs fsxtest $SCRATCHDEV jfs 10000 -#fsx-xfs fsxtest $SCRATCHDEV xfs 10000 -#fsx-reiserfs fsxtest $SCRATCHDEV reiserfs 10000 - diff --git a/runtest/hugetlb b/runtest/hugetlb index 299c07ac..0896d3c9 100755 --- a/runtest/hugetlb +++ b/runtest/hugetlb @@ -35,6 +35,7 @@ hugemmap29 hugemmap29 hugemmap30 hugemmap30 hugemmap31 hugemmap31 hugemmap32 hugemmap32 +hugemmap34 hugemmap34 hugemmap05_1 hugemmap05 -m hugemmap05_2 hugemmap05 -s hugemmap05_3 hugemmap05 -s -m @@ -55,3 +56,4 @@ hugeshmget01 hugeshmget01 -i 10 hugeshmget02 hugeshmget02 -i 10 hugeshmget03 hugeshmget03 -i 10 hugeshmget05 hugeshmget05 -i 10 +hugeshmget06 hugeshmget06 -i 10 diff --git a/runtest/io b/runtest/io deleted file mode 100755 index cd51cce3..00000000 --- a/runtest/io +++ /dev/null @@ -1,3 +0,0 @@ -#AIO01 & AIO02 tests to be run -aio01 aio01 -aio02 aio02 diff --git a/runtest/ipc b/runtest/ipc deleted file mode 100755 index db7f7bed..00000000 --- a/runtest/ipc +++ /dev/null @@ -1,30 +0,0 @@ -#DESCRIPTION:Interprocess communication stress -# These tests use tests/pipeio to put pipes (named or unnamed) through a workout -# -pipeio_1 pipeio -T pipeio_1 -c 5 -s 4090 -i 100 -b -f x80 -# spawns 5 children to write 100 chunks of 4090 bytes to a named pipe -# using blocking I/O -#pipeio_2 pipeio -T pipeio_2 -c 5 -s 4090 -i 100 -f x80 -# spawns 5 children to write 100 chunks of 4090 bytes to a named pipe -# using non-blocking I/O -# This test hits EAGAIN, which pipeio doesn't handle at the moment -pipeio_3 pipeio -T pipeio_3 -c 5 -s 4090 -i 100 -u -b -f x80 -# spawns 5 children to write 100 chunks of 4090 bytes to an unnamed pipe -# using blocking I/O -pipeio_4 pipeio -T pipeio_4 -c 5 -s 4090 -i 100 -u -f x80 -# spawns 5 children to write 100 chunks of 4090 bytes to an unnamed pipe -# using non-blocking I/O -pipeio_5 pipeio -T pipeio_5 -c 5 -s 5000 -i 10 -b -f x80 -# spawns 5 children to write 10 chunks of 5000 bytes to a named pipe -# using blocking I/O -pipeio_6 pipeio -T pipeio_6 -c 5 -s 5000 -i 10 -b -u -f x80 -# spawns 5 children to write 10 chunks of 5000 bytes to an unnamed pipe -# using blocking I/O -#pipeio_7 pipeio -T pipeio_7 -c 5 -s 5000 -i 10 -f x80 -# spawns 5 children to write 10 chunks of 5000 bytes to a named pipe -# using non-blocking I/O -# This test hits EAGAIN, which pipeio doesn't handle at the moment -pipeio_8 pipeio -T pipeio_8 -c 5 -s 5000 -i 10 -u -f x80 -# spawns 5 children to write 10 chunks of 5000 bytes to an unnamed pipe -# using non-blocking I/O - diff --git a/runtest/kernel_misc b/runtest/kernel_misc index abb75eba..78f00d30 100755 --- a/runtest/kernel_misc +++ b/runtest/kernel_misc @@ -1,3 +1,4 @@ +cn_pec_sh cn_pec.sh kmsg01 kmsg01 fw_load fw_load rtc01 rtc01 @@ -14,3 +15,4 @@ zram01 zram01.sh zram02 zram02.sh zram03 zram03 umip_basic_test umip_basic_test +aslr01 aslr01 diff --git a/runtest/kvm b/runtest/kvm index 4094a21a..5c285e65 100644 --- a/runtest/kvm +++ b/runtest/kvm @@ -1,4 +1,8 @@ -kvm_pagefault01 kvm_pagefault01 kvm_svm01 kvm_svm01 kvm_svm02 kvm_svm02 kvm_svm03 kvm_svm03 +kvm_svm04 kvm_svm04 +kvm_vmx01 kvm_vmx01 +kvm_vmx02 kvm_vmx02 +# Tests below may interfere with bug reproducibility +kvm_pagefault01 kvm_pagefault01 diff --git a/runtest/ltp-aiodio.part3 b/runtest/ltp-aiodio.part3 index d53e836b..decf2f6e 100755 --- a/runtest/ltp-aiodio.part3 +++ b/runtest/ltp-aiodio.part3 @@ -1,51 +1,21 @@ -# fname: this filename is Required (no default) -# -# -FSX032 fsx-linux -l 500000 -r 4096 -t 4096 -w 4096 -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX033 fsx-linux -l 500000 -r 4096 -t 2048 -w 2048 -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX034 fsx-linux -l 500000 -r 4096 -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX035 fsx-linux -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX036 fsx-linux -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX037 fsx-linux -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX038 fsx-linux -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX039 fsx-linux -N 10000 $TMPDIR/aiodio.$$/junkfile -FSX040 fsx-linux -N 10000 -o 1024 $TMPDIR/aiodio.$$/junkfile -FSX041 fsx-linux -N 10000 -o 2048 $TMPDIR/aiodio.$$/junkfile -FSX042 fsx-linux -N 10000 -o 4096 $TMPDIR/aiodio.$$/junkfile -FSX043 fsx-linux -N 10000 -o 8192 $TMPDIR/aiodio.$$/junkfile -FSX044 fsx-linux -N 10000 -o 16384 $TMPDIR/aiodio.$$/junkfile -FSX045 fsx-linux -N 10000 -o 32768 $TMPDIR/aiodio.$$/junkfile -FSX046 fsx-linux -N 10000 -o 128000 $TMPDIR/aiodio.$$/junkfile -FSX047 fsx-linux -N 10000 -o 1024 $TMPDIR/aiodio.$$/junkfile -FSX048 fsx-linux -N 10000 -o 2048 $TMPDIR/aiodio.$$/junkfile -FSX049 fsx-linux -N 10000 -o 4096 $TMPDIR/aiodio.$$/junkfile -FSX050 fsx-linux -N 10000 -o 8192 $TMPDIR/aiodio.$$/junkfile -FSX051 fsx-linux -N 10000 -o 16384 $TMPDIR/aiodio.$$/junkfile -FSX052 fsx-linux -N 10000 -o 32768 $TMPDIR/aiodio.$$/junkfile -FSX053 fsx-linux -N 10000 -o 128000 $TMPDIR/aiodio.$$/junkfile -FSX054 fsx-linux -N 10000 -o 1024 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX055 fsx-linux -N 10000 -o 2048 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile -FSX056 fsx-linux -N 10000 -o 4096 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX057 fsx-linux -N 10000 -o 8192 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile -FSX058 fsx-linux -N 10000 -o 16384 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX059 fsx-linux -N 10000 -o 32768 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile -FSX060 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX061 fsx-linux -N 10000 -o 32768 $TMPDIR/aiodio.$$/junkfile -FSX062 fsx-linux -N 10000 -o 128000 $TMPDIR/aiodio.$$/junkfile -FSX063 fsx-linux -N 10000 -o 1024 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX064 fsx-linux -N 10000 -o 2048 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile -FSX065 fsx-linux -N 10000 -o 4096 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX066 fsx-linux -N 10000 -o 8192 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile -FSX067 fsx-linux -N 10000 -o 16384 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX068 fsx-linux -N 10000 -o 32768 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile -FSX069 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX070 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile -FSX071 fsx-linux -N 10000 -o 16384 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile1 -FSX072 fsx-linux -N 10000 -o 32768 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile2 -FSX073 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile3 -FSX074 fsx-linux -N 10000 -o 16384 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile4 -FSX075 fsx-linux -N 10000 -o 32768 -l 500000 -r 4096 -t 2048 -w 2048 $TMPDIR/aiodio.$$/junkfile5 -FSX076 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 4096 $TMPDIR/aiodio.$$/junkfile6 -FSX077 fsx-linux -N 10000 $TMPDIR/aiodio.$$/junkfile7 -FSX078 fsx-linux -N 100000 $TMPDIR/aiodio.$$/junkfile8 -FSX079 fsx-linux -N 100000 $TMPDIR/aiodio.$$/junkfile9 +fsx01 fsx-linux -l 500000 -r 4096 -t 4096 -w 4096 -N 10000 +fsx02 fsx-linux -l 500000 -r 4096 -t 2048 -w 2048 -N 10000 +fsx03 fsx-linux -l 500000 -r 4096 -N 10000 +fsx04 fsx-linux -N 10000 +fsx05 fsx-linux -N 10000 -o 1024 +fsx06 fsx-linux -N 10000 -o 2048 +fsx07 fsx-linux -N 10000 -o 4096 +fsx08 fsx-linux -N 10000 -o 8192 +fsx09 fsx-linux -N 10000 -o 16384 +fsx10 fsx-linux -N 10000 -o 32768 +fsx12 fsx-linux -N 10000 -o 128000 +fsx13 fsx-linux -N 10000 -o 1024 -l 500000 -r 4096 -t 4096 -w 4096 +fsx14 fsx-linux -N 10000 -o 2048 -l 500000 -r 4096 -t 2048 -w 2048 +fsx15 fsx-linux -N 10000 -o 4096 -l 500000 -r 4096 -t 4096 -w 4096 +fsx16 fsx-linux -N 10000 -o 8192 -l 500000 -r 4096 -t 2048 -w 2048 +fsx17 fsx-linux -N 10000 -o 16384 -l 500000 -r 4096 -t 4096 -w 4096 +fsx18 fsx-linux -N 10000 -o 32768 -l 500000 -r 4096 -t 2048 -w 2048 +fsx19 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 4096 +fsx20 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 40963 +fsx21 fsx-linux -N 10000 -o 128000 -l 500000 -r 4096 -t 4096 -w 40966 +fsx22 fsx-linux -N 100000 diff --git a/runtest/ltp-aiodio.part4 b/runtest/ltp-aiodio.part4 index c31bef93..de00b8a7 100755 --- a/runtest/ltp-aiodio.part4 +++ b/runtest/ltp-aiodio.part4 @@ -1,6 +1,6 @@ -# 3/2006 -# Malcles Jacky: this file is a copy from Ridgeway Marty's aio script -# goal: all aio tests using the same framework +aio01 aio01 +aio02 aio02 + #Running dio_sparse & dirty tests DI000 dirty DS000 dio_sparse diff --git a/runtest/mm b/runtest/mm index f288fed3..41d624ad 100755 --- a/runtest/mm +++ b/runtest/mm @@ -1,13 +1,9 @@ #DESCRIPTION:Memory Mgmt tests -mm01 mmap001 -m 10000 +mmap21_01 mmap21 -m 10000 # 40 Mb mmap() test. # Creates a 10000 page mmap, touches all of the map, sync's it, and # munmap()s it. -mm02 mmap001 -# simple mmap() test. -#mm03 mmap001 -i 0 -I 1 -m 100 -# repetitive mmapping test. -# Creates a one page map repetitively for one minute. +mmap21_02 mmap21 mtest01 mtest01 -p80 mtest01w mtest01 -p80 -w @@ -32,7 +28,6 @@ shmt02 shmt02 shmt03 shmt03 shmt04 shmt04 shmt05 shmt05 -shmt06 shmt06 shmt07 shmt07 shmt08 shmt08 shmt09 shmt09 @@ -47,16 +42,15 @@ mmapstress03 mmapstress03 mmapstress04 mmapstress04 mmapstress05 mmapstress05 mmapstress06 mmapstress06 20 -mmapstress07 TMPFILE=`mktemp /tmp/example.XXXXXXXXXXXX`; mmapstress07 $TMPFILE +mmapstress07 TMPFILE=`mktemp $TMPDIR/example.XXXXXXXXXXXX`; mmapstress07 $TMPFILE mmapstress08 mmapstress08 mmapstress09 mmapstress09 -p 20 -t 0.2 mmapstress10 mmapstress10 -p 20 -t 0.2 mmap10 mmap10 -mmap10_1 mmap10 -a -mmap10_2 mmap10 -s -mmap10_3 mmap10 -a -s -mmap10_4 mmap10 -a -s -i 60 +mmap10_1 mmap10 -i 60 + +kallsyms kallsyms ksm01 ksm01 ksm01_1 ksm01 -u 128 @@ -70,8 +64,10 @@ ksm05 ksm05 -I 10 ksm06 ksm06 ksm06_1 ksm06 -n 10 ksm06_2 ksm06 -n 8000 +ksm07 ksm07 cpuset01 cpuset01 +cpuset02 cpuset02 oom01 oom01 oom02 oom02 diff --git a/runtest/net.nfs b/runtest/net.nfs index 15a96001..fef993da 100755 --- a/runtest/net.nfs +++ b/runtest/net.nfs @@ -2,107 +2,140 @@ # # PLEASE READ THE README FILE network/README.md BEFORE RUNNING THESE. # -nfs3_01 nfs01.sh -v 3 -t udp -nfs3t_01 nfs01.sh -v 3 -t tcp -nfs4_01 nfs01.sh -v 4 -t tcp -nfs41_01 nfs01.sh -v 4.1 -t tcp -nfs42_01 nfs01.sh -v 4.2 -t tcp -nfs3_ipv6_01 nfs01.sh -6 -v 3 -t udp -nfs3t_ipv6_01 nfs01.sh -6 -v 3 -t tcp -nfs4_ipv6_01 nfs01.sh -6 -v 4 -t tcp -nfs41_ipv6_01 nfs01.sh -6 -v 4.1 -t tcp -nfs42_ipv6_01 nfs01.sh -6 -v 4.2 -t tcp +nfs01_v30_ip4u nfs01.sh -v 3 -t udp +nfs01_v30_ip4t nfs01.sh -v 3 -t tcp +nfs01_v40_ip4t nfs01.sh -v 4 -t tcp +nfs01_v41_ip4t nfs01.sh -v 4.1 -t tcp +nfs01_v42_ip4t nfs01.sh -v 4.2 -t tcp +nfs01_v30_ip6u nfs01.sh -6 -v 3 -t udp +nfs01_v30_ip6t nfs01.sh -6 -v 3 -t tcp +nfs01_v40_ip6t nfs01.sh -6 -v 4 -t tcp +nfs01_v41_ip6t nfs01.sh -6 -v 4.1 -t tcp +nfs01_v42_ip6t nfs01.sh -6 -v 4.2 -t tcp -nfs3_02 nfs02.sh -v 3 -t udp -nfs3t_02 nfs02.sh -v 3 -t tcp -nfs4_02 nfs02.sh -v 4 -t tcp -nfs41_02 nfs02.sh -v 4.1 -t tcp -nfs42_02 nfs02.sh -v 4.2 -t tcp -nfs3_ipv6_02 nfs02.sh -6 -v 3 -t udp -nfs3t_ipv6_02 nfs02.sh -6 -v 3 -t tcp -nfs4_ipv6_02 nfs02.sh -6 -v 4 -t tcp -nfs41_ipv6_02 nfs02.sh -6 -v 4.1 -t tcp -nfs42_ipv6_02 nfs02.sh -6 -v 4.2 -t tcp +nfs02_v30_ip4u nfs02.sh -v 3 -t udp +nfs02_v30_ip4t nfs02.sh -v 3 -t tcp +nfs02_v40_ip4t nfs02.sh -v 4 -t tcp +nfs02_v41_ip4t nfs02.sh -v 4.1 -t tcp +nfs02_v42_ip4t nfs02.sh -v 4.2 -t tcp +nfs02_v30_ip6u nfs02.sh -6 -v 3 -t udp +nfs02_v30_ip6t nfs02.sh -6 -v 3 -t tcp +nfs02_v40_ip6t nfs02.sh -6 -v 4 -t tcp +nfs02_v41_ip6t nfs02.sh -6 -v 4.1 -t tcp +nfs02_v42_ip6t nfs02.sh -6 -v 4.2 -t tcp -nfs3_03 nfs03.sh -v 3 -t udp -nfs3t_03 nfs03.sh -v 3 -t tcp -nfs4_03 nfs03.sh -v 4 -t tcp -nfs41_03 nfs03.sh -v 4.1 -t tcp -nfs42_03 nfs03.sh -v 4.2 -t tcp -nfs3_ipv6_03 nfs03.sh -6 -v 3 -t udp -nfs3t_ipv6_03 nfs03.sh -6 -v 3 -t tcp -nfs4_ipv6_03 nfs03.sh -6 -v 4 -t tcp -nfs41_ipv6_03 nfs03.sh -6 -v 4.1 -t tcp -nfs42_ipv6_03 nfs03.sh -6 -v 4.2 -t tcp +nfs03_v30_ip4u nfs03.sh -v 3 -t udp +nfs03_v30_ip4t nfs03.sh -v 3 -t tcp +nfs03_v40_ip4t nfs03.sh -v 4 -t tcp +nfs03_v41_ip4t nfs03.sh -v 4.1 -t tcp +nfs03_v42_ip4t nfs03.sh -v 4.2 -t tcp +nfs03_v30_ip6u nfs03.sh -6 -v 3 -t udp +nfs03_v30_ip6t nfs03.sh -6 -v 3 -t tcp +nfs03_v40_ip6t nfs03.sh -6 -v 4 -t tcp +nfs03_v41_ip6t nfs03.sh -6 -v 4.1 -t tcp +nfs03_v42_ip6t nfs03.sh -6 -v 4.2 -t tcp -nfs3_04 nfs04.sh -v 3 -t udp -nfs3t_04 nfs04.sh -v 3 -t tcp -nfs4_04 nfs04.sh -v 4 -t tcp -nfs41_04 nfs04.sh -v 4.1 -t tcp -nfs42_04 nfs04.sh -v 4.2 -t tcp -nfs3_ipv6_04 nfs04.sh -6 -v 3 -t udp -nfs3t_ipv6_04 nfs04.sh -6 -v 3 -t tcp -nfs4_ipv6_04 nfs04.sh -6 -v 4 -t tcp -nfs41_ipv6_04 nfs04.sh -6 -v 4.1 -t tcp -nfs42_ipv6_04 nfs04.sh -6 -v 4.2 -t tcp +nfs04_v30_ip4u nfs04.sh -v 3 -t udp +nfs04_v30_ip4t nfs04.sh -v 3 -t tcp +nfs04_v40_ip4t nfs04.sh -v 4 -t tcp +nfs04_v41_ip4t nfs04.sh -v 4.1 -t tcp +nfs04_v42_ip4t nfs04.sh -v 4.2 -t tcp +nfs04_v30_ip6u nfs04.sh -6 -v 3 -t udp +nfs04_v30_ip6t nfs04.sh -6 -v 3 -t tcp +nfs04_v40_ip6t nfs04.sh -6 -v 4 -t tcp +nfs04_v41_ip6t nfs04.sh -6 -v 4.1 -t tcp +nfs04_v42_ip6t nfs04.sh -6 -v 4.2 -t tcp -nfs3_05 nfs05.sh -v 3 -t udp -nfs3t_05 nfs05.sh -v 3 -t tcp -nfs4_05 nfs05.sh -v 4 -t tcp -nfs41_05 nfs05.sh -v 4.1 -t tcp -nfs42_05 nfs05.sh -v 4.2 -t tcp -nfs3_ipv6_05 nfs05.sh -6 -v 3 -t udp -nfs3t_ipv6_05 nfs05.sh -6 -v 3 -t tcp -nfs4_ipv6_05 nfs05.sh -6 -v 4 -t tcp -nfs41_ipv6_05 nfs05.sh -6 -v 4.1 -t tcp -nfs42_ipv6_05 nfs05.sh -6 -v 4.2 -t tcp +nfs05_v30_ip4u nfs05.sh -v 3 -t udp +nfs05_v30_ip4t nfs05.sh -v 3 -t tcp +nfs05_v40_ip4t nfs05.sh -v 4 -t tcp +nfs05_v41_ip4t nfs05.sh -v 4.1 -t tcp +nfs05_v42_ip4t nfs05.sh -v 4.2 -t tcp +nfs05_v30_ip6u nfs05.sh -6 -v 3 -t udp +nfs05_v30_ip6t nfs05.sh -6 -v 3 -t tcp +nfs05_v40_ip6t nfs05.sh -6 -v 4 -t tcp +nfs05_v41_ip6t nfs05.sh -6 -v 4.1 -t tcp +nfs05_v42_ip6t nfs05.sh -6 -v 4.2 -t tcp -nfs01_06 nfs06.sh -v "3,3,3,4,4,4" -t "udp,udp,tcp,tcp,tcp,tcp" -nfs02_06 nfs06.sh -v "3,4,4.1,4.2,4.2,4.2" -t "tcp,tcp,tcp,tcp,tcp,tcp" -nfs03_ipv6_06 nfs06.sh -6 -v "4,4.1,4.1,4.2,4.2,4.2" -t "tcp,tcp,tcp,tcp,tcp,tcp" +nfs06_v30_v40_ip4 nfs06.sh -v "3,3,3,4,4,4" -t "udp,udp,tcp,tcp,tcp,tcp" +nfs06_vall_ip4t nfs06.sh -v "3,4,4.1,4.2,4.2,4.2" -t "tcp,tcp,tcp,tcp,tcp,tcp" +nfs06_v4x_ip6t nfs06.sh -6 -v "4,4.1,4.1,4.2,4.2,4.2" -t "tcp,tcp,tcp,tcp,tcp,tcp" -nfs3_07 nfs07.sh -v 3 -t udp -nfs3t_07 nfs07.sh -v 3 -t tcp -nfs4_07 nfs07.sh -v 4 -t tcp -nfs41_07 nfs07.sh -v 4.1 -t tcp -nfs42_07 nfs07.sh -v 4.2 -t tcp -nfs3_ipv6_07 nfs07.sh -6 -v 3 -t udp -nfs3t_ipv6_07 nfs07.sh -6 -v 3 -t tcp -nfs4_ipv6_07 nfs07.sh -6 -v 4 -t tcp -nfs41_ipv6_07 nfs07.sh -6 -v 4.1 -t tcp -nfs42_ipv6_07 nfs07.sh -6 -v 4.2 -t tcp +nfs07_v30_ip4u nfs07.sh -v 3 -t udp +nfs07_v30_ip4t nfs07.sh -v 3 -t tcp +nfs07_v40_ip4t nfs07.sh -v 4 -t tcp +nfs07_v41_ip4t nfs07.sh -v 4.1 -t tcp +nfs07_v42_ip4t nfs07.sh -v 4.2 -t tcp +nfs07_v30_ip6u nfs07.sh -6 -v 3 -t udp +nfs07_v30_ip6t nfs07.sh -6 -v 3 -t tcp +nfs07_v40_ip6t nfs07.sh -6 -v 4 -t tcp +nfs07_v41_ip6t nfs07.sh -6 -v 4.1 -t tcp +nfs07_v42_ip6t nfs07.sh -6 -v 4.2 -t tcp -nfs3_08 nfs08.sh -v 3 -t udp -nfs3t_08 nfs08.sh -v 3 -t tcp -nfs4_08 nfs08.sh -v 4 -t tcp -nfs41_08 nfs08.sh -v 4.1 -t tcp -nfs42_08 nfs08.sh -v 4.2 -t tcp -nfs3_ipv6_08 nfs08.sh -6 -v 3 -t udp -nfs3t_ipv6_08 nfs08.sh -6 -v 3 -t tcp -nfs4_ipv6_08 nfs08.sh -6 -v 4 -t tcp -nfs41_ipv6_08 nfs08.sh -6 -v 4.1 -t tcp -nfs42_ipv6_08 nfs08.sh -6 -v 4.2 -t tcp +nfs08_v30_ip4u nfs08.sh -v 3 -t udp +nfs08_v30_ip4t nfs08.sh -v 3 -t tcp +nfs08_v40_ip4t nfs08.sh -v 4 -t tcp +nfs08_v41_ip4t nfs08.sh -v 4.1 -t tcp +nfs08_v42_ip4t nfs08.sh -v 4.2 -t tcp +nfs08_v30_ip6u nfs08.sh -6 -v 3 -t udp +nfs08_v30_ip6t nfs08.sh -6 -v 3 -t tcp +nfs08_v40_ip6t nfs08.sh -6 -v 4 -t tcp +nfs08_v41_ip6t nfs08.sh -6 -v 4.1 -t tcp +nfs08_v42_ip6t nfs08.sh -6 -v 4.2 -t tcp -nfslock3_01 nfslock01.sh -v 3 -t udp -nfslock3t_01 nfslock01.sh -v 3 -t tcp -nfslock4_01 nfslock01.sh -v 4 -t tcp -nfslock41_01 nfslock01.sh -v 4.1 -t tcp -nfslock42_01 nfslock01.sh -v 4.2 -t tcp -nfslock3_ipv6_01 nfslock01.sh -6 -v 3 -t udp -nfslock3t_ipv6_01 nfslock01.sh -6 -v 3 -t tcp -nfslock4_ipv6_01 nfslock01.sh -6 -v 4 -t tcp -nfslock41_ipv6_01 nfslock01.sh -6 -v 4.1 -t tcp -nfslock42_ipv6_01 nfslock01.sh -6 -v 4.2 -t tcp +nfs09_v30_ip4u nfs09.sh -v 3 -t udp +nfs09_v30_ip4t nfs09.sh -v 3 -t tcp +nfs09_v40_ip4t nfs09.sh -v 4 -t tcp +nfs09_v41_ip4t nfs09.sh -v 4.1 -t tcp +nfs09_v42_ip4t nfs09.sh -v 4.2 -t tcp +nfs09_v30_ip6u nfs09.sh -6 -v 3 -t udp +nfs09_v30_ip6t nfs09.sh -6 -v 3 -t tcp +nfs09_v40_ip6t nfs09.sh -6 -v 4 -t tcp +nfs09_v41_ip6t nfs09.sh -6 -v 4.1 -t tcp +nfs09_v42_ip6t nfs09.sh -6 -v 4.2 -t tcp -nfsstat3_01 nfsstat01.sh +nfs10_v30_ip4u nfs10.sh -v 3 -t udp +nfs10_v30_ip4t nfs10.sh -v 3 -t tcp +nfs10_v40_ip4t nfs10.sh -v 4 -t tcp +nfs10_v41_ip4t nfs10.sh -v 4.1 -t tcp +nfs10_v42_ip4t nfs10.sh -v 4.2 -t tcp +nfs10_v30_ip6u nfs10.sh -6 -v 3 -t udp +nfs10_v30_ip6t nfs10.sh -6 -v 3 -t tcp +nfs10_v40_ip6t nfs10.sh -6 -v 4 -t tcp +nfs10_v41_ip6t nfs10.sh -6 -v 4.1 -t tcp +nfs10_v42_ip6t nfs10.sh -6 -v 4.2 -t tcp -nfsx3 fsx.sh -v 3 -t udp -nfsx3t fsx.sh -v 3 -t tcp -nfsx4 fsx.sh -v 4 -t tcp -nfsx41 fsx.sh -v 4.1 -t tcp -nfsx42 fsx.sh -v 4.2 -t tcp -nfsx3_ipv6 fsx.sh -6 -v 3 -t udp -nfsx3t_ipv6 fsx.sh -6 -v 3 -t tcp -nfsx4_ipv6 fsx.sh -6 -v 4 -t tcp -nfsx41_ipv6 fsx.sh -6 -v 4.1 -t tcp -nfsx42_ipv6 fsx.sh -6 -v 4.2 -t tcp +nfslock01_v30_ip4u nfslock01.sh -v 3 -t udp +nfslock01_v30_ip4t nfslock01.sh -v 3 -t tcp +nfslock01_v40_ip4t nfslock01.sh -v 4 -t tcp +nfslock01_v41_ip4t nfslock01.sh -v 4.1 -t tcp +nfslock01_v42_ip4t nfslock01.sh -v 4.2 -t tcp +nfslock01_v30_ip6u nfslock01.sh -6 -v 3 -t udp +nfslock01_v30_ip6t nfslock01.sh -6 -v 3 -t tcp +nfslock01_v40_ip6t nfslock01.sh -6 -v 4 -t tcp +nfslock01_v41_ip6t nfslock01.sh -6 -v 4.1 -t tcp +nfslock01_v42_ip6t nfslock01.sh -6 -v 4.2 -t tcp + +nfsstat01_v30_ip4u nfsstat01.sh -v 3 -t udp +nfsstat01_v30_ip4t nfsstat01.sh -v 3 -t tcp +nfsstat01_v40_ip4t nfsstat01.sh -v 4 -t tcp +nfsstat01_v41_ip4t nfsstat01.sh -v 4.1 -t tcp +nfsstat01_v42_ip4t nfsstat01.sh -v 4.2 -t tcp +nfsstat01_v30_ip6u nfsstat01.sh -6 -v 3 -t udp +nfsstat01_v30_ip6t nfsstat01.sh -6 -v 3 -t tcp +nfsstat01_v40_ip6t nfsstat01.sh -6 -v 4 -t tcp +nfsstat01_v41_ip6t nfsstat01.sh -6 -v 4.1 -t tcp +nfsstat01_v42_ip6t nfsstat01.sh -6 -v 4.2 -t tcp + +nfsstat02 nfsstat02.sh + +fsx_v30_ip4u fsx.sh -v 3 -t udp +fsx_v30_ip4t fsx.sh -v 3 -t tcp +fsx_v40_ip4t fsx.sh -v 4 -t tcp +fsx_v41_ip4t fsx.sh -v 4.1 -t tcp +fsx_v42_ip4t fsx.sh -v 4.2 -t tcp +fsx_v30_ip6u fsx.sh -6 -v 3 -t udp +fsx_v30_ip6t fsx.sh -6 -v 3 -t tcp +fsx_v40_ip6t fsx.sh -6 -v 4 -t tcp +fsx_v41_ip6t fsx.sh -6 -v 4.1 -t tcp +fsx_v42_ip6t fsx.sh -6 -v 4.2 -t tcp diff --git a/runtest/net.tcp_cmds b/runtest/net.tcp_cmds index 7e142de1..aba02fb3 100755 --- a/runtest/net.tcp_cmds +++ b/runtest/net.tcp_cmds @@ -5,16 +5,12 @@ ipneigh01_arp ipneigh01.sh -c arp ipneigh01_ip ipneigh01.sh -c ip arping01 arping01.sh -clockdiff01 clockdiff01.sh -ftp ftp01.sh -host host01.sh netstat netstat01.sh ping01 ping01.sh ping02 ping02.sh sendfile sendfile01.sh tc01 tc01.sh tcpdump tcpdump01.sh -telnet telnet01.sh iptables iptables01.sh nft nft01.sh dhcpd dhcpd_tests.sh @@ -22,4 +18,3 @@ dnsmasq dnsmasq_tests.sh iproute ip_tests.sh tracepath01 tracepath01.sh traceroute01 traceroute01.sh -xinetd xinetd_tests.sh diff --git a/runtest/pty b/runtest/pty index df207415..4b1abe7a 100755 --- a/runtest/pty +++ b/runtest/pty @@ -6,6 +6,13 @@ pty04 pty04 pty05 pty05 pty06 pty06 pty07 pty07 +pty08 pty08 +pty09 pty09 ptem01 ptem01 +ptem02 ptem02 +ptem03 ptem03 +ptem04 ptem04 +ptem05 ptem05 +ptem06 ptem06 hangup01 hangup01 diff --git a/runtest/sched b/runtest/sched index 172fe417..ecedd1a9 100755 --- a/runtest/sched +++ b/runtest/sched @@ -11,8 +11,12 @@ hackbench01 hackbench 50 process 1000 hackbench02 hackbench 20 thread 1000 starvation starvation +proc_sched_rt01 proc_sched_rt01 + sched_cli_serv run_sched_cliserv.sh # Run this stress test for 2 minutes sched_stress sched_stress.sh autogroup01 autogroup01 + +sched_football sched_football diff --git a/runtest/smoketest b/runtest/smoketest index 83eebfe7..538ee06f 100755 --- a/runtest/smoketest +++ b/runtest/smoketest @@ -7,10 +7,7 @@ fork01 fork01 time01 time01 wait02 wait02 write01 write01 -symlink01 symlink01 -stat04 symlink01 -T stat04 -utime01A symlink01 -T utime01 -rename01A symlink01 -T rename01 +stat04 stat04 splice02 splice02 -s 20 df01_sh df01.sh shell_test01 echo "SUCCESS" | shell_pipe01.sh diff --git a/runtest/syscalls b/runtest/syscalls index 4f1ee1f3..4b284f27 100755 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -3,6 +3,7 @@ abort01 abort01 accept01 accept01 accept02 accept02 +accept03 accept03 accept4_01 accept4_01 @@ -30,6 +31,8 @@ alarm05 alarm05 alarm06 alarm06 alarm07 alarm07 +arch_prctl01 arch_prctl01 + bind01 bind01 bind02 bind02 bind03 bind03 @@ -59,16 +62,21 @@ capset04 capset04 cacheflush01 cacheflush01 +cachestat01 cachestat01 +cachestat02 cachestat02 +cachestat03 cachestat03 +cachestat04 cachestat04 + chdir01 chdir01 -chdir01A symlink01 -T chdir01 chdir04 chdir04 chmod01 chmod01 -chmod01A symlink01 -T chmod01 chmod03 chmod03 chmod05 chmod05 chmod06 chmod06 chmod07 chmod07 +chmod08 chmod08 +chmod09 chmod09 chown01 chown01 chown01_16 chown01_16 @@ -104,6 +112,7 @@ leapsec01 leapsec01 clock_settime01 clock_settime01 clock_settime02 clock_settime02 clock_settime03 clock_settime03 +clock_settime04 clock_settime04 clone01 clone01 clone02 clone02 @@ -184,6 +193,7 @@ epoll_pwait02 epoll_pwait02 epoll_pwait03 epoll_pwait03 epoll_pwait04 epoll_pwait04 epoll_pwait05 epoll_pwait05 +epoll_pwait06 epoll_pwait06 eventfd01 eventfd01 eventfd02 eventfd02 @@ -236,6 +246,11 @@ fallocate06 fallocate06 fsetxattr01 fsetxattr01 fsetxattr02 fsetxattr02 +file_attr01 file_attr01 +file_attr02 file_attr02 +file_attr03 file_attr03 +file_attr04 file_attr04 + #posix_fadvise test cases posix_fadvise01 posix_fadvise01 posix_fadvise01_64 posix_fadvise01_64 @@ -259,6 +274,10 @@ fchmod06 fchmod06 #fchmodat test cases fchmodat01 fchmodat01 +fchmodat02 fchmodat02 + +fchmodat2_01 fchmodat2_01 +fchmodat2_02 fchmodat2_02 fchown01 fchown01 fchown01_16 fchown01_16 @@ -274,6 +293,7 @@ fchown05_16 fchown05_16 #fchownat test case fchownat01 fchownat01 fchownat02 fchownat02 +fchownat03 fchownat03 fcntl01 fcntl01 fcntl01_64 fcntl01_64 @@ -327,8 +347,6 @@ fcntl26 fcntl26 fcntl26_64 fcntl26_64 fcntl27 fcntl27 fcntl27_64 fcntl27_64 -fcntl28 fcntl28 -fcntl28_64 fcntl28_64 fcntl29 fcntl29 fcntl29_64 fcntl29_64 fcntl30 fcntl30 @@ -351,6 +369,8 @@ fcntl38 fcntl38 fcntl38_64 fcntl38_64 fcntl39 fcntl39 fcntl39_64 fcntl39_64 +fcntl40 fcntl40 +fcntl40_64 fcntl40_64 fdatasync01 fdatasync01 fdatasync02 fdatasync02 @@ -372,6 +392,7 @@ flock02 flock02 flock03 flock03 flock04 flock04 flock06 flock06 +flock07 flock07 fmtmsg01 fmtmsg01 @@ -379,12 +400,12 @@ fork01 fork01 fork03 fork03 fork04 fork04 fork05 fork05 -fork06 fork06 +fork06 fork_procs -n 1000 fork07 fork07 fork08 fork08 fork09 fork09 fork10 fork10 -fork11 fork11 +fork11 fork_procs -n 100 fork13 fork13 fork14 fork14 @@ -437,6 +458,7 @@ futimesat01 futimesat01 getcontext01 getcontext01 getcpu01 getcpu01 +getcpu02 getcpu02 getcwd01 getcwd01 getcwd02 getcwd02 @@ -448,8 +470,6 @@ getdents02 getdents02 getdomainname01 getdomainname01 -getdtablesize01 getdtablesize01 - getegid01 getegid01 getegid01_16 getegid01_16 getegid02 getegid02 @@ -475,6 +495,7 @@ gethostbyname_r01 gethostbyname_r01 gethostid01 gethostid01 gethostname01 gethostname01 +gethostname02 gethostname02 getitimer01 getitimer01 getitimer02 getitimer02 @@ -501,6 +522,7 @@ getrandom01 getrandom01 getrandom02 getrandom02 getrandom03 getrandom03 getrandom04 getrandom04 +getrandom05 getrandom05 getresgid01 getresgid01 getresgid01_16 getresgid01_16 @@ -538,6 +560,7 @@ getsockopt01 getsockopt01 getsockopt02 getsockopt02 gettid01 gettid01 +gettid02 gettid02 gettimeofday01 gettimeofday01 gettimeofday02 gettimeofday02 @@ -557,7 +580,7 @@ init_module01 init_module01 init_module02 init_module02 #Needs tty device. -#ioctl02 ioctl02 -D /dev/tty0 +#ioctl02 ioctl02 -d /dev/tty0 # Introducing ioctl tests for all /dev/tty* devices ioctl01 ioctl01 @@ -569,6 +592,7 @@ ioctl06 ioctl06 ioctl07 ioctl07 ioctl08 ioctl08 ioctl09 ioctl09 +ioctl10 ioctl10 ioctl_loop01 ioctl_loop01 ioctl_loop02 ioctl_loop02 @@ -588,6 +612,20 @@ ioctl_ns07 ioctl_ns07 ioctl_sg01 ioctl_sg01 +ioctl_ficlone01 ioctl_ficlone01 +ioctl_ficlone02 ioctl_ficlone02 +ioctl_ficlone03 ioctl_ficlone03 +ioctl_ficlonerange01 ioctl_ficlonerange01 +ioctl_ficlonerange02 ioctl_ficlonerange02 +ioctl_fiemap01 ioctl_fiemap01 + +ioctl_pidfd01 ioctl_pidfd01 +ioctl_pidfd02 ioctl_pidfd02 +ioctl_pidfd03 ioctl_pidfd03 +ioctl_pidfd04 ioctl_pidfd04 +ioctl_pidfd05 ioctl_pidfd05 +ioctl_pidfd06 ioctl_pidfd06 + inotify_init1_01 inotify_init1_01 inotify_init1_02 inotify_init1_02 @@ -627,6 +665,7 @@ fanotify20 fanotify20 fanotify21 fanotify21 fanotify22 fanotify22 fanotify23 fanotify23 +fanotify24 fanotify24 ioperm01 ioperm01 ioperm02 ioperm02 @@ -673,27 +712,32 @@ kill02 kill02 kill03 kill03 kill05 kill05 kill06 kill06 -kill07 kill07 kill08 kill08 -kill09 kill09 kill10 kill10 kill11 kill11 kill12 kill12 kill13 kill13 +landlock01 landlock01 +landlock02 landlock02 +landlock03 landlock03 +landlock04 landlock04 +landlock05 landlock05 +landlock06 landlock06 +landlock07 landlock07 +landlock08 landlock08 +landlock09 landlock09 +landlock10 landlock10 + lchown01 lchown01 lchown01_16 lchown01_16 lchown02 lchown02 -lchown03 lchown03 lchown02_16 lchown02_16 -lchown03_16 lchown03_16 lgetxattr01 lgetxattr01 lgetxattr02 lgetxattr02 -link01 symlink01 -T link01 link02 link02 -link03 link03 link04 link04 link05 link05 link08 link08 @@ -704,9 +748,15 @@ linkat02 linkat02 listen01 listen01 +listmount01 listmount01 +listmount02 listmount02 +listmount03 listmount03 +listmount04 listmount04 + listxattr01 listxattr01 listxattr02 listxattr02 listxattr03 listxattr03 +listxattr04 listxattr04 llistxattr01 llistxattr01 llistxattr02 llistxattr02 @@ -723,12 +773,19 @@ lseek02 lseek02 lseek07 lseek07 lseek11 lseek11 -lstat01A symlink01 -T lstat01 -lstat01A_64 symlink01 -T lstat01_64 +lsm_get_self_attr01 lsm_get_self_attr01 +lsm_get_self_attr02 lsm_get_self_attr02 +lsm_get_self_attr03 lsm_get_self_attr03 +lsm_list_modules01 lsm_list_modules01 +lsm_list_modules02 lsm_list_modules02 +lsm_set_self_attr01 lsm_set_self_attr01 + lstat01 lstat01 lstat01_64 lstat01_64 lstat02 lstat02 lstat02_64 lstat02_64 +lstat03 lstat03 +lstat03_64 lstat03_64 mallinfo02 mallinfo02 @@ -757,7 +814,6 @@ mkdir02 mkdir02 mkdir03 mkdir03 mkdir04 mkdir04 mkdir05 mkdir05 -mkdir05A symlink01 -T mkdir05 mkdir09 mkdir09 #mkdirat test cases @@ -782,12 +838,12 @@ mlock01 mlock01 mlock02 mlock02 mlock03 mlock03 -i 20 mlock04 mlock04 +mlock05 mlock05 mlock201 mlock201 mlock202 mlock202 mlock203 mlock203 -qmm01 mmap001 -m 1 mmap01 mmap01 mmap02 mmap02 mmap03 mmap03 @@ -807,10 +863,12 @@ mmap17 mmap17 mmap18 mmap18 mmap19 mmap19 mmap20 mmap20 +mmap21_01 mmap21 -m 1 +mmap21_02 mmap21 +mmap22 mmap22 modify_ldt01 modify_ldt01 modify_ldt02 modify_ldt02 -modify_ldt03 modify_ldt03 mount01 mount01 mount02 mount02 @@ -819,11 +877,14 @@ mount04 mount04 mount05 mount05 mount06 mount06 mount07 mount07 +mount08 mount08 mount_setattr01 mount_setattr01 +mount_setattr02 mount_setattr02 move_mount01 move_mount01 move_mount02 move_mount02 +move_mount03 move_mount03 move_pages01 move_pages01 move_pages02 move_pages02 @@ -860,18 +921,19 @@ mremap04 mremap04 mremap05 mremap05 mremap06 mremap06 +mseal01 mseal01 +mseal02 mseal02 + msgctl01 msgctl01 msgctl02 msgctl02 msgctl03 msgctl03 msgctl04 msgctl04 msgctl05 msgctl05 msgctl06 msgctl06 -msgstress01 msgstress01 -msgstress02 msgstress02 -msgstress03 msgstress03 -msgstress04 msgstress04 msgctl12 msgctl12 +msgstress01 msgstress01 + msgget01 msgget01 msgget02 msgget02 msgget03 msgget03 @@ -902,8 +964,8 @@ munlock02 munlock02 munlockall01 munlockall01 munmap01 munmap01 -munmap02 munmap02 munmap03 munmap03 +munmap04 munmap04 nanosleep01 nanosleep01 nanosleep02 nanosleep02 @@ -922,7 +984,6 @@ nice04 nice04 nice05 nice05 open01 open01 -open01A symlink01 -T open01 open02 open02 open03 open03 open04 open04 @@ -935,6 +996,7 @@ open11 open11 open12 open12 open13 open13 open14 open14 +open15 open15 openat01 openat01 openat02 openat02 @@ -966,14 +1028,15 @@ madvise08 madvise08 madvise09 madvise09 madvise10 madvise10 madvise11 madvise11 +madvise12 madvise12 newuname01 newuname01 pathconf01 pathconf01 +pathconf02 pathconf02 pause01 pause01 pause02 pause02 -pause03 pause03 personality01 personality01 personality02 personality02 @@ -1004,11 +1067,22 @@ pipe11 pipe11 pipe12 pipe12 pipe13 pipe13 pipe14 pipe14 +pipe15 pipe15 pipe2_01 pipe2_01 pipe2_02 pipe2_02 pipe2_04 pipe2_04 +# Interprocess communication stress tests +pipeio_1 pipeio -T pipeio_1 -c 5 -s 4090 -i 100 -b -f x80 +pipeio_2 pipeio -T pipeio_2 -c 5 -s 4090 -i 100 -f x80 +pipeio_3 pipeio -T pipeio_3 -c 5 -s 4090 -i 100 -u -b -f x80 +pipeio_4 pipeio -T pipeio_4 -c 5 -s 4090 -i 100 -u -f x80 +pipeio_5 pipeio -T pipeio_5 -c 5 -s 5000 -i 10 -b -f x80 +pipeio_6 pipeio -T pipeio_6 -c 5 -s 5000 -i 10 -b -u -f x80 +pipeio_7 pipeio -T pipeio_7 -c 5 -s 5000 -i 10 -f x80 +pipeio_8 pipeio -T pipeio_8 -c 5 -s 5000 -i 10 -u -f x80 + pivot_root01 pivot_root01 poll01 poll01 @@ -1019,7 +1093,6 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 -prctl04 prctl04 prctl05 prctl05 prctl06 prctl06 prctl07 prctl07 @@ -1073,8 +1146,7 @@ ptrace02 ptrace02 ptrace03 ptrace03 ptrace04 ptrace04 ptrace05 ptrace05 -# Broken test; See: testcases/kernel/syscalls/ptrace/Makefile for more details. -#ptrace06 ptrace06 +ptrace06 ptrace06 ptrace07 ptrace07 ptrace08 ptrace08 ptrace09 ptrace09 @@ -1124,7 +1196,6 @@ readahead02 readahead02 readdir01 readdir01 readdir21 readdir21 -readlink01A symlink01 -T readlink01 readlink01 readlink01 readlink03 readlink03 @@ -1157,7 +1228,6 @@ removexattr01 removexattr01 removexattr02 removexattr02 rename01 rename01 -rename01A symlink01 -T rename01 rename03 rename03 rename04 rename04 rename05 rename05 @@ -1170,6 +1240,7 @@ rename11 rename11 rename12 rename12 rename13 rename13 rename14 rename14 +rename15 rename15 #renameat test cases renameat01 renameat01 @@ -1182,11 +1253,11 @@ request_key02 request_key02 request_key03 request_key03 request_key04 request_key04 request_key05 request_key05 +request_key06 request_key06 rmdir01 rmdir01 rmdir02 rmdir02 rmdir03 rmdir03 -rmdir03A symlink01 -T rmdir03 rt_sigaction01 rt_sigaction01 rt_sigaction02 rt_sigaction02 @@ -1194,6 +1265,7 @@ rt_sigaction03 rt_sigaction03 rt_sigprocmask01 rt_sigprocmask01 rt_sigprocmask02 rt_sigprocmask02 rt_sigqueueinfo01 rt_sigqueueinfo01 +rt_sigqueueinfo02 rt_sigqueueinfo02 rt_sigsuspend01 rt_sigsuspend01 rt_sigtimedwait01 rt_sigtimedwait01 rt_tgsigqueueinfo01 rt_tgsigqueueinfo01 @@ -1227,6 +1299,7 @@ sched_getscheduler02 sched_getscheduler02 sched_setscheduler01 sched_setscheduler01 sched_setscheduler02 sched_setscheduler02 sched_setscheduler03 sched_setscheduler03 +sched_setscheduler04 sched_setscheduler04 sched_yield01 sched_yield01 @@ -1237,6 +1310,8 @@ sched_setattr01 sched_setattr01 sched_getattr01 sched_getattr01 sched_getattr02 sched_getattr02 +seccomp01 seccomp01 + select01 select01 select02 select02 select03 select03 @@ -1301,6 +1376,7 @@ set_mempolicy04 set_mempolicy04 set_robust_list01 set_robust_list01 set_thread_area01 set_thread_area01 +set_thread_area02 set_thread_area02 set_tid_address01 set_tid_address01 setdomainname01 setdomainname01 @@ -1424,6 +1500,7 @@ setsockopt06 setsockopt06 setsockopt07 setsockopt07 setsockopt08 setsockopt08 setsockopt09 setsockopt09 +setsockopt10 setsockopt10 settimeofday01 settimeofday01 settimeofday02 settimeofday02 @@ -1442,6 +1519,7 @@ setxattr03 setxattr03 shmat01 shmat01 shmat02 shmat02 shmat03 shmat03 +shmat04 shmat04 shmctl01 shmctl01 shmctl02 shmctl02 @@ -1461,6 +1539,9 @@ shmget04 shmget04 shmget05 shmget05 shmget06 shmget06 +shutdown01 shutdown01 +shutdown02 shutdown02 + sigaction01 sigaction01 sigaction02 sigaction02 @@ -1478,6 +1559,7 @@ signal05 signal05 signal06 signal06 signalfd01 signalfd01 +signalfd02 signalfd02 signalfd4_01 signalfd4_01 signalfd4_02 signalfd4_02 @@ -1489,6 +1571,7 @@ sigprocmask01 sigprocmask01 sigrelse01 sigrelse01 sigsuspend01 sigsuspend01 +sigsuspend02 sigsuspend02 sigtimedwait01 sigtimedwait01 @@ -1512,6 +1595,10 @@ splice02 splice02 splice03 splice03 splice04 splice04 splice05 splice05 +splice06 splice06 +splice07 splice07 +splice08 splice08 +splice09 splice09 tee01 tee01 tee02 tee02 @@ -1524,8 +1611,18 @@ stat02 stat02 stat02_64 stat02_64 stat03 stat03 stat03_64 stat03_64 -stat04 symlink01 -T stat04 -stat04_64 symlink01 -T stat04_64 +stat04 stat04 +stat04_64 stat04_64 + +statmount01 statmount01 +statmount02 statmount02 +statmount03 statmount03 +statmount04 statmount04 +statmount05 statmount05 +statmount06 statmount06 +statmount07 statmount07 +statmount08 statmount08 +statmount09 statmount09 statfs01 statfs01 statfs01_64 statfs01_64 @@ -1552,11 +1649,9 @@ swapon03 swapon03 #Exclusive syscall() for POWER6 machines only switch01 endian_switch01 -symlink01 symlink01 symlink02 symlink02 symlink03 symlink03 symlink04 symlink04 -symlink05 symlink05 #symlinkat test cases symlinkat01 symlinkat01 @@ -1603,7 +1698,6 @@ times03 times03 timerfd01 timerfd01 timerfd02 timerfd02 -timerfd03 timerfd03 timerfd04 timerfd04 timerfd_create01 timerfd_create01 timerfd_gettime01 timerfd_gettime01 @@ -1644,16 +1738,20 @@ uname01 uname01 uname02 uname02 uname04 uname04 -unlink01 symlink01 -T unlink01 unlink05 unlink05 unlink07 unlink07 unlink08 unlink08 +unlink09 unlink09 +unlink10 unlink10 #unlinkat test cases unlinkat01 unlinkat01 unshare01 unshare01 unshare02 unshare02 +unshare03 unshare03 +unshare04 unshare04 +unshare05 unshare05 # # These tests require an unmounted block device @@ -1673,12 +1771,12 @@ ustat01 ustat01 ustat02 ustat02 utime01 utime01 -utime01A symlink01 -T utime01 utime02 utime02 utime03 utime03 utime04 utime04 utime05 utime05 utime06 utime06 +utime07 utime07 utimes01 utimes01 @@ -1705,10 +1803,8 @@ wait402 wait402 wait403 wait403 waitpid01 waitpid01 -waitpid02 waitpid02 waitpid03 waitpid03 waitpid04 waitpid04 -waitpid05 waitpid05 waitpid06 waitpid06 waitpid07 waitpid07 waitpid08 waitpid08 diff --git a/runtest/syscalls-ipc b/runtest/syscalls-ipc index df41140a..8960c487 100755 --- a/runtest/syscalls-ipc +++ b/runtest/syscalls-ipc @@ -4,12 +4,10 @@ msgctl03 msgctl03 msgctl04 msgctl04 msgctl05 msgctl05 msgctl06 msgctl06 -msgstress01 msgstress01 -msgstress02 msgstress02 -msgstress03 msgstress03 -msgstress04 msgstress04 msgctl12 msgctl12 +msgstress01 msgstress01 + msgget01 msgget01 msgget02 msgget02 msgget03 msgget03 @@ -49,6 +47,7 @@ semop03 semop03 shmat01 shmat01 shmat02 shmat02 +shmat04 shmat04 shmctl01 shmctl01 shmctl02 shmctl02 diff --git a/scenario_groups/default b/scenario_groups/default index 68bd5300..0e76b2be 100755 --- a/scenario_groups/default +++ b/scenario_groups/default @@ -1,9 +1,7 @@ syscalls fs fs_perms_simple -fsx dio -io mm ipc irq @@ -14,10 +12,7 @@ pty containers fs_bind controllers -filecaps -cap_bounds fcntl-locktests -connectors power_management_tests hugetlb commands diff --git a/scripts/calctimeouts.py b/scripts/calctimeouts.py new file mode 100644 index 00000000..18886704 --- /dev/null +++ b/scripts/calctimeouts.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2025 Cyril Hrubis +# Copyright (c) 2025 Andrea Cervesato +""" +This script parses JSON results from kirk and LTP metadata in order to +calculate timeouts for tests based on the results file. +It can also patch tests automatically and replace the calculated timeout. +""" + +import re +import os +import json +import argparse + +# The test runtime is multiplied by this to get a timeout +TIMEOUT_MUL = 1.2 + + +def _sed(fname, expr, replace): + """ + Pythonic version of sed command. + """ + content = [] + matcher = re.compile(expr) + + with open(fname, 'r', encoding="utf-8") as data: + for line in data: + match = matcher.search(line) + if not match: + content.append(line) + else: + content.append(replace) + + with open(fname, 'w', encoding="utf-8") as data: + data.writelines(content) + + +def _patch(ltp_dir, fname, new_timeout, override): + """ + If `override` is True, it patches a test file, searching for timeout and + replacing it with `new_timeout`. + """ + orig_timeout = None + file_path = os.path.join(ltp_dir, fname) + + with open(file_path, 'r', encoding="utf-8") as c_source: + matcher = re.compile(r'\s*.timeout\s*=\s*(\d+).') + for line in c_source: + match = matcher.search(line) + if not match: + continue + + timeout = match.group(1) + orig_timeout = int(timeout) + + if orig_timeout: + if orig_timeout < new_timeout or override: + print(f"CHANGE {fname} timeout {orig_timeout} -> {new_timeout}") + _sed(file_path, r".timeout = [0-9]*,\n", + f"\t.timeout = {new_timeout},\n") + else: + print(f"KEEP {fname} timeout {orig_timeout} (new {new_timeout})") + else: + print(f"ADD {fname} timeout {new_timeout}") + _sed(file_path, + "static struct tst_test test = {", + "static struct tst_test test = {\n" + f"\t.timeout = {new_timeout},\n") + + +def _patch_all(ltp_dir, timeouts, override): + """ + Patch all tests. + """ + for timeout in timeouts: + if timeout['path']: + _patch(ltp_dir, timeout['path'], timeout['timeout'], override) + + +def _print_table(timeouts): + """ + Print the timeouts table. + """ + timeouts.sort(key=lambda x: x['timeout'], reverse=True) + + total = 0 + + print("Old library tests\n-----------------\n") + for timeout in timeouts: + if not timeout['newlib']: + print(f"{timeout['name']:30s} {timeout['timeout']}") + total += 1 + + print(f"\n\t{total} tests in total") + + total = 0 + + print("\nNew library tests\n-----------------\n") + for timeout in timeouts: + if timeout['newlib']: + print(f"{timeout['name']:30s} {timeout['timeout']}") + total += 1 + + print(f"\n\t{total} tests in total") + + +def _parse_data(ltp_dir, results_path): + """ + Parse results data and metadata, then it generates timeouts data. + """ + timeouts = [] + results = None + metadata = None + + with open(results_path, 'r', encoding="utf-8") as file: + results = json.load(file) + + metadata_path = os.path.join(ltp_dir, 'metadata', 'ltp.json') + with open(metadata_path, 'r', encoding="utf-8") as file: + metadata = json.load(file) + + for test in results['results']: + name = test['test_fqn'] + duration = test['test']['duration'] + + # if test runs for all_filesystems, normalize runtime to one filesystem + filesystems = max(1, test['test']['log'].count('TINFO: Formatting /')) + + # check if test is new library test + test_is_newlib = name in metadata['tests'] + + # store test file path + path = None + if test_is_newlib: + path = metadata['tests'][name]['fname'] + + test_has_runtime = False + if test_is_newlib: + # filter out tests with runtime + test_has_runtime = 'runtime' in metadata['tests'][name] + + # timer tests define runtime dynamically in timer library + test_has_runtime = 'sample' in metadata['tests'][name] + + # select tests that does not have runtime and which are executed + # for a long time + if not test_has_runtime and duration >= 0.5: + data = {} + data["name"] = name + data["timeout"] = int(TIMEOUT_MUL * duration/filesystems + 0.5) + data["newlib"] = test_is_newlib + data["path"] = path + + timeouts.append(data) + + return timeouts + + +def _file_exists(filepath): + """ + Check if the given file path exists. + """ + if not os.path.isfile(filepath): + raise argparse.ArgumentTypeError( + f"The file '{filepath}' does not exist.") + return filepath + + +def _dir_exists(dirpath): + """ + Check if the given directory path exists. + """ + if not os.path.isdir(dirpath): + raise argparse.ArgumentTypeError( + f"The directory '{dirpath}' does not exist.") + return dirpath + + +def run(): + """ + Entry point of the script. + """ + parser = argparse.ArgumentParser( + description="Script to calculate LTP tests timeouts") + + parser.add_argument( + '-l', + '--ltp-dir', + type=_dir_exists, + help='LTP source code directory', + default='..') + + parser.add_argument( + '-r', + '--results', + type=_file_exists, + required=True, + help='kirk results.json file location') + + parser.add_argument( + '-o', + '--override', + default=False, + action='store_true', + help='Always override test timeouts') + + parser.add_argument( + '-p', + '--patch', + default=False, + action='store_true', + help='Patch tests with updated timeout') + + parser.add_argument( + '-t', + '--print-table', + default=True, + action='store_true', + help='Print table with suggested timeouts') + + args = parser.parse_args() + + timeouts = _parse_data(args.ltp_dir, args.results) + + if args.print_table: + _print_table(timeouts) + + if args.patch: + _patch_all(args.ltp_dir, timeouts, args.override) + + +if __name__ == "__main__": + run() diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8c74d07b..21d9c9fe 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1,5 +1,5 @@ #!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only # # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp (the ugly bit) diff --git a/testcases/Makefile b/testcases/Makefile index 662d4b1e..b45fca6d 100755 --- a/testcases/Makefile +++ b/testcases/Makefile @@ -6,17 +6,8 @@ top_srcdir ?= .. include $(top_srcdir)/include/mk/env_pre.mk -# XXX (garrcoop): -# kdump shouldn't be compiled by default, because it's runtime based and will -# crash the build host (the tests need to be fixed to just build, not run). -FILTER_OUT_DIRS := kdump - ifneq ($(WITH_OPEN_POSIX_TESTSUITE),yes) FILTER_OUT_DIRS += open_posix_testsuite endif -ifneq ($(WITH_REALTIME_TESTSUITE),yes) -FILTER_OUT_DIRS += realtime -endif - include $(top_srcdir)/include/mk/generic_trunk_target.mk diff --git a/testcases/commands/du/du01.sh b/testcases/commands/du/du01.sh index 7977fd46..04e9d761 100755 --- a/testcases/commands/du/du01.sh +++ b/testcases/commands/du/du01.sh @@ -59,19 +59,17 @@ du_test() fi } -block_size=512 -page_size=$(tst_getconf PAGESIZE) -if [ "$page_size" -lt 1024 ]; then - tst_brk TBROK "Page size < 1024" -fi -page_size=$((page_size / 1024)) +block_size_default=512 + +block_size=$(stat -f --format="%s" .) +block_size=$((block_size / 1024)) # The output could be different in some systems, if we use du to # estimate file space usage with the same filesystem and the same size. # So we use the approximate value to check. check1="^10[2-3][0-9][0-9][[:space:]]\." check2="^10[2-3][0-9][0-9][[:space:]]testfile" -check3="^\(0\|${page_size}\)[[:space:]]\.\/testdir\/testsymlink" +check3="^\(0\|${block_size}\)[[:space:]]\.\/testdir\/testsymlink" check5="^20[4-6][0-9][0-9][[:space:]]\." check7="^10[4-5][0-9][0-9]\{4\}[[:space:]]\." check9="^10[2-3][0-9][0-9][[:space:]]total" @@ -88,8 +86,8 @@ do_test() 2) du_test "du testfile" ${check2};; 3) du_test "du -a" ${check3};; 4) du_test "du --all" ${check3};; - 5) du_test "du -B ${block_size}" ${check5};; - 6) du_test "du --block-size=${block_size}" ${check5};; + 5) du_test "du -B ${block_size_default}" ${check5};; + 6) du_test "du --block-size=${block_size_default}" ${check5};; 7) du_test "du -b" ${check7};; 8) du_test "du --bytes" ${check7};; 9) du_test "du -c" ${check9};; diff --git a/testcases/commands/eject/eject-tests.sh b/testcases/commands/eject/eject-tests.sh deleted file mode 100755 index 76a667aa..00000000 --- a/testcases/commands/eject/eject-tests.sh +++ /dev/null @@ -1,147 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) International Business Machines Corp., 2001 -# Copyright (c) 2016 Cyril Hrubis -# Author: Manoj Iyer -# -# Tests basic functionality of eject command. - -TST_CNT=4 -TST_SETUP=setup -TST_CLEANUP=cleanup -TST_TESTFUNC=test -TST_NEEDS_TMPDIR=1 -TST_NEEDS_ROOT=1 -TST_NEEDS_CMDS="eject" - -setup() -{ - CD_DRIVE="/dev/cdrom" - - if ! [ -e "$CD_DRIVE" ]; then - tst_brk TCONF "There is no "$CD_DRIVE"" - fi - - if grep -q "$CD_DRIVE" /proc/mounts; then - tst_brk TCONF "$CD_DRIVE is already mounted" - fi - - ROD mkdir "cdrom" -} - -cleanup() -{ - # We have to use the mount point since /dev/cdrom may be link to - # /dev/sr0 and because of that /dev/cdrom is not listed in /proc/mounts - tst_umount "$PWD/cdrom" -} - -test1() -{ - EXPECT_PASS eject -d \> eject.out - - if grep -q "eject: default device:" eject.out; then - tst_res TPASS "Eject listed default device" - else - tst_res TFAIL "Eject failed to list default device" - cat eject.out - fi -} - -test2() -{ - EXPECT_PASS eject -v $CD_DRIVE \> eject.out - - if grep -q "CD-ROM eject command succeeded" eject.out; then - # Close the tray if it is supported. - eject -t $CD_DRIVE > /dev/null 2>&1 - tst_res TPASS "Drive successfully ejected" - else - tst_res TFAIL "Eject failed" - cat eject.out - fi -} - -mount_cdrom() -{ - local tries=100 - - # Wait for the drive to spin up the disk - while [ $tries -gt 0 ]; do - eject_check_tray $CD_DRIVE - if [ $? -eq 4 ]; then - break - fi - tst_sleep 100ms - tries=$((tries-1)) - done - - mount "$CD_DRIVE" cdrom/ > mount.out 2>&1 - if [ $? -eq 32 ]; then - tst_res TCONF "Failed to mount $CD_DRIVE, no disk in drive?" - cat mount.out - return 0 - fi - - tst_res TINFO "$CD_DRIVE mounted sucessfully" - - return 1 -} - -test3() -{ - if mount_cdrom; then - return - fi - - test2 - - if grep -q "$CD_DRIVE" /proc/mounts; then - tst_res TFAIL "$CD_DRIVE is stil moutned" - else - tst_res TPASS "$CD_DRIVE umounted successfully" - fi -} - -test4() -{ - if mount_cdrom; then - return - fi - - EXPECT_PASS eject -a on $CD_DRIVE - - eject_check_tray $CD_DRIVE - if [ $? -eq 2 ]; then - tst_brk TBROK "$CD_DRIVE is mounted but tray is open" - fi - - EXPECT_PASS umount $CD_DRIVE - - eject_check_tray $CD_DRIVE - if [ $? -eq 2 ]; then - tst_res TPASS "$CD_DRIVE was auto-ejected" - else - tst_res TFAIL "$CD_DRIVE was not auto-ejected" - fi - - EXPECT_PASS eject -a off $CD_DRIVE - - eject -t $CD_DRIVE > /dev/null 2>&1 - - if mount_cdrom; then - return - fi - - EXPECT_PASS umount $CD_DRIVE - - eject_check_tray $CD_DRIVE - if [ $? -eq 2 ]; then - tst_res TFAIL "$CD_DRIVE was auto-ejected" - else - tst_res TPASS "$CD_DRIVE was not auto-ejected" - fi -} - -. tst_test.sh -tst_run diff --git a/testcases/commands/eject/eject_check_tray.c b/testcases/commands/eject/eject_check_tray.c deleted file mode 100755 index 07e81534..00000000 --- a/testcases/commands/eject/eject_check_tray.c +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************/ -/* */ -/* Copyright (c) International Business Machines Corp., 2001 */ -/* Jan 8 2003 - Created - Manoj Iyer manjo@mail.utexas.edu */ -/* */ -/* 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 */ -/******************************************************************************/ - -/* - * - * Description: This program checks the status of the cdrom drive, it will - * return the status as to if the cdrom device is open or is - * ready for use. - */ - -#include -#include -#include -#include -#include - -/* - * Exit Vaules: - * 0 - No information. - * 1 - No disk in the drive. - * 2 - CD tray is open. - * 3 - CD drive not ready. - * 4 - CD disk in drive & drive closed. - */ -int main(int argc, char *argv[]) -{ - int fd; - - if (argc != 2) - exit(-1); - - if ((fd = open(argv[1], O_RDONLY | O_NONBLOCK)) == -1) - exit(-2); - - exit(ioctl(fd, CDROM_DRIVE_STATUS)); -} diff --git a/testcases/commands/gzip/gzip_tests.sh b/testcases/commands/gzip/gzip_tests.sh index fdc933ea..3262c555 100755 --- a/testcases/commands/gzip/gzip_tests.sh +++ b/testcases/commands/gzip/gzip_tests.sh @@ -82,7 +82,8 @@ test1() gzip -r tst_gzip.tmp > tst_gzip.err 2>&1 if [ $? -ne 0 ]; then cat tst_gzip.err - tst_brk TFAIL "Test #1: gzip -r failed" + tst_res TFAIL "Test #1: gzip -r failed" + return fi tst_res TINFO "Test #1: creating output file" diff --git a/testcases/commands/insmod/.gitignore b/testcases/commands/insmod/.gitignore index 0e19fb3f..ba8fca18 100755 --- a/testcases/commands/insmod/.gitignore +++ b/testcases/commands/insmod/.gitignore @@ -1,9 +1,6 @@ *.ko *.mod.c -*.ko.cmd -*.mod.cmd -*.mod.o.cmd -*.o.cmd -.built-in.a.cmd +*.cmd Module.symvers modules.order +modules.livepatch diff --git a/testcases/commands/insmod/insmod01.sh b/testcases/commands/insmod/insmod01.sh index 992b4a05..bc4f50e0 100755 --- a/testcases/commands/insmod/insmod01.sh +++ b/testcases/commands/insmod/insmod01.sh @@ -30,6 +30,11 @@ cleanup() do_test() { + tst_check_kconfigs "CONFIG_MODULE_SIG_FORCE=y" + if [ $? -eq 0 ] || grep module.sig_enforce -qw /proc/cmdline; then + tst_brk TCONF "module signature is enforced, skipping test" + fi + insmod "$TST_MODPATH" if [ $? -ne 0 ]; then tst_res TFAIL "insmod failed" diff --git a/testcases/commands/logrotate/00_Descriptions.txt b/testcases/commands/logrotate/00_Descriptions.txt deleted file mode 100755 index 07ffcd3e..00000000 --- a/testcases/commands/logrotate/00_Descriptions.txt +++ /dev/null @@ -1,2 +0,0 @@ -logrotate01 - test basic functionality of logrotate. use logrotate -f to force rotation. 1. rotate /var/log/tst_logfile file. 2. compresses it. diff --git a/testcases/commands/logrotate/Makefile b/testcases/commands/logrotate/Makefile deleted file mode 100755 index c3a701b8..00000000 --- a/testcases/commands/logrotate/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# -# commands/logrotate testcases Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Ngie Cooper, July 2009 -# - -top_srcdir ?= ../../.. - -include $(top_srcdir)/include/mk/env_pre.mk - -INSTALL_TARGETS := logrotate_tests.sh - -MAKE_TARGETS := - -include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/commands/logrotate/logrotate_tests.sh b/testcases/commands/logrotate/logrotate_tests.sh deleted file mode 100755 index 10523771..00000000 --- a/testcases/commands/logrotate/logrotate_tests.sh +++ /dev/null @@ -1,381 +0,0 @@ -#!/bin/sh -################################################################################ -## ## -## Copyright (c) International Business Machines Corp., 2001 ## -## ## -## 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 ## -## ## -################################################################################ -# -# File : logrotate_tests.sh -# -# Description: Test Basic functionality of logrotate command. -# Test #1: Test that logrotate -f rotates the logfile -# as per the specifications in the conf file. Create a file -# tst_logfile in /var/log/. Create a conf file such that this -# logfile is set for rotation every week. Execute the command -# logrotate -f , check to see if it forced rotation. -# Test #2: Check if logrotate running as a cronjob will rotate a -# logfile when it exceeds a specific size. Create two cronjobs -# 1. runs a command to log a string to a logfile. 2. runs -# logrotate every minute. The conf file specifies -# that the rotation happen only if the log file exceeds 2k file -# size. -# -# Author: Manoj Iyer, manjo@mail.utexas.edu -# -# History: Dec 23 2002 - Created - Manoj Iyer. -# Dec 24 2002 - Added - Test #2 - Test to run logrotate as a -# cron job. -# Feb 28 2003 - Fixed - Modified testcase to use functions. -# -# Function: chk_ifexists -# -# Description: - Check if command required for this test exits. -# -# Input: - $1 - calling test case. -# - $2 - command that needs to be checked. -# -# Return: - zero on success. -# - non-zero on failure. -chk_ifexists() -{ - RC=0 - - which $2 > $LTPTMP/tst_logrotate.err 2>&1 || RC=$? - if [ $RC -ne 0 ] - then - tst_brkm TBROK NULL "$1: command $2 not found." - fi - return $RC -} - - -# Function: init -# -# Description: - Check if command required for this test exits. -# - Create temporary directories required for this test. -# - Initialize global variables. -# -# Return: - zero on success. -# - non-zero on failure. -init() -{ - # Initialize global variables. - export RC=0 - export TST_TOTAL=2 - export TCID="logrotate" - export TST_COUNT=0 - - # Inititalize cleanup function. - trap "cleanup" 0 - - # create the temporary directory used by this testcase - if [ -z $TMP ] - then - LTPTMP=/tmp/tst_logrotate.$$ - else - LTPTMP=$TMP/tst_logrotate.$$ - fi - - mkdir -p $LTPTMP > /dev/null 2>&1 || RC=$? - if [ $RC -ne 0 ] - then - tst_brkm TBROK "INIT: Unable to create temporary directory" - return $RC - fi - - # check if commands tst_*, logrotate, awk and file exists. - chk_ifexists INIT tst_resm || return $RC - chk_ifexists INIT logrotate || return $RC - chk_ifexists INIT awk || return $RC - chk_ifexists INIT file || return $RC - - return $RC -} - - -# Function: cleanup -# -# Description: - remove temporaty files and directories. Stop all jobs stated -# by this testcase. -# -# Return: - zero on success. -# - non-zero on failure. -cleanup() -{ - #remove all cronjobs that were installed. - tst_resm TINFO "CLEAN: removing all cron jobs." - crontab -r > /dev/null 2>&1 - - # remove all the temporary files created by this test. - tst_resm TINFO "CLEAN: removing $LTPTMP" - rm -fr $LTPTMP -} - - -# Function: test01 -# -# Description: - Test that logrotate logrotate will rotate the logfile -# according to the specifications in the config file. -# - create a config file that will rotate the /var/log/tst_logfile -# file. -# - use force option to force logrotate to cause the log file to -# be rotated. -# - compress the file after rotation. -# -# Return: - zero on success. -# - non-zero on failure. -test01() -{ - count=0 - files=" " - filesize=0 - - TCID=logrotate01 - TST_COUNT=1 - - tst_resm TINFO "Test #1: create a configfile $LTPTMP/var_mesg.config" - tst_resm TINFO "Test #1: use logrotate -f to force rotation" - tst_resm TINFO "Test #1: this will rotate the log file according to" - tst_resm TINFO "Test #1: the specification in the configfile." - tst_resm TINFO "Test #1: 1. rotate /var/log/tst_logfile file." - tst_resm TINFO "Test #1: 2. compresses it." - - # Check if syslog group exists - local group="syslog" - grep -q $group /etc/group || group="root" - - # create config file. - cat >$LTPTMP/tst_logrotate.conf <<-EOF - #****** Begin Config file ******* - # create new (empty) log files after rotating old ones - create - - # compress the log files - compress - - /var/log/tst_logfile { - su root $group - rotate 5 - weekly - } - #****** End Config file ******* - EOF - - # create a log file in /var/log/ - cat >/var/log/tst_logfile <<-EOF - #****** Begin Log File ******** - # This is a dummy log file. - #****** End Log File ******** - EOF - - while [ $count -lt 10 ] - do - echo "This a dummy log file used to test logrotate command." >> \ - /var/log/tst_logfile - count=$(( $count+1 )) - done - - # remove all old-n-stale logfiles. - for files in /var/log/tst_logfile.* - do - rm -f $files > /dev/null 2>&1 - done - - chmod 644 $LTPTMP/tst_logrotate.conf - logrotate -fv $LTPTMP/tst_logrotate.conf > $LTPTMP/tst_logrotate.out 2>&1 \ - || RC=$? - if [ $RC -eq 0 ] - then - # check if config file $LTPTMP/tst_logrotate.conf is read - # check if /etc/logrotate.d is included/ - # check if 5 rotations are forced. - # check if compression is done. - grep "reading config file $LTPTMP/tst_logrotate.conf" \ - $LTPTMP/tst_logrotate.out > $LTPTMP/tst_logrotate.err 2>&1 || RC=$? - grep "forced from command line (5 rotations)" \ - $LTPTMP/tst_logrotate.out > $LTPTMP/tst_logrotate.err 2>&1 || RC=$? - egrep "compressing new|log with" \ - $LTPTMP/tst_logrotate.out > $LTPTMP/tst_logrotate.err 2>&1 || RC=$? - if [ $RC -ne 0 ] - then - tst_res TFAIL $LTPTMP/tst_logrotate.err \ - "Test #1: logrotate command failed. Reason:" - else - # Check if compressed log file is created. - if [ -f /var/log/tst_logfile.1.gz ] - then - file /var/log/tst_logfile.1.gz | grep "gzip compressed data" \ - > $LTPTMP/tst_logrotate.out 2>&1 || RC=$? - if [ $RC -eq 0 ] - then - tst_resm TPASS \ - "Test #1: logrotate created a compressed file." - else - tst_res TFAIL $LTPTMP/tst_logrotate.out \ - "Test #1: Failed to create a compressed file. Reason:" - fi - return $RC - else - tst_res TFAIL $LTPTMP/tst_logrotate.out \ - "Test #1: Failed create /var/log/tst_logfile.1.gz. Reason:" - return $RC - fi - fi - else - tst_res TFAIL $LTPTMP/tst_logrotate.out \ - "Test #1: logrotate command exited with $RC return code. Output:" - fi - return $RC -} - - -test02() -{ -# Test #2 -# Test that logrotate logrotate will rotate the logfile if the logfile -# exceeds a certain size. -# - create a config file that will rotate the /var/log/tst_largelogfile. -# - run logrotate in a cron job that runs every minute. -# - add messages to the logfile until it gets rotated when a re-dittermined -# size is reached. - -export TCID=logrotate02 -export TST_COUNT=2 -RC=0 - -tst_resm TINFO "Test #2: create a configfile $LTPTMP/tst_largelog.conf" -tst_resm TINFO "Test #2: logrotate $LTPTMP/tst_largelog.conf - cronjob" -tst_resm TINFO "Test #2: set to rotate tst_largelogfile when size > 2K" - - -# create config file. -cat >$LTPTMP/tst_largelog.conf </var/log/tst_largelogfile <$LTPTMP/tst_logrotate.cron < /dev/null 2>&1 - -tst_resm TINFO "Test #2: Installing cron job to run logrotate" -crontab $LTPTMP/tst_logrotate.cron > $LTPTMP/tst_logrotate.out 2>&1 || RC=$? -if [ $RC -ne 0 ] -then - echo "Exit status of crontab command: $RC" >> tst_logrotate.out 2>/dev/null - tst_brk TBROK $LTPTMP/tst_logrotate.out NULL \ - "Test #2: crontab Broke while installing cronjob. Reason:" - TFAILCNT=$(( $TFAILCN+1 )) -else - tst_resm TINFO "Test #2: Cronjob installed successfully" -fi - -# cron job to increase the log file size. -cat >$LTPTMP/tst_addtolog.cron <>/var/log/tst_largelogfile 2>/dev/null -EOF - -tst_resm TINFO "Test #2: Installing cron job to increase logsize" -crontab $LTPTMP/tst_addtolog.cron > $LTPTMP/tst_logrotate.out 2>&1 || RC=$? -if [ $RC -ne 0 ] -then - echo "Exit status of crontab command: $RC" >> tst_logrotate.out 2>/dev/null - tst_brk TBROK $LTPTMP/tst_logrotate.out NULL \ - "Test #2: crontab Broke while installing cronjob. Reason:" - TFAILCNT=$(( $TFAILCN+1 )) -else - tst_resm TINFO "Test #2: Cronjob installed successfully" -fi - -# let cron jobs get started. -sleep 10s - -# increase the log file size. - -# wait for the /var/log/tst_largelogfile to be filled to a size greater than 2k -tst_resm TINFO "Test #2: Checking if file size is > 2k" -tst_resm TINFO "Test #2: Pls be patient this will take some time." -tst_resm TINFO "Test #2: or killall -9 logrotate02 to skip.." -if [ -f `which awk` ] -then - while [ $filesize -lt 2046 ] - do - filesize=`ls -l /var/log/tst_largelogfile | awk '{print $5}'` - done - # wait for 1m and check if logrotate has rotated the logfile. The cron job - # that does a logrotate runs every 1 minute so give the cron a minute... - sleep 1m -else - tst_resm TINFO "Test #2: No AWK installed ... sleeping for 10mts" - sleep 10m -fi - - -if [ -f /var/log/tst_largelogfile.1.gz ] -then - file /var/log/tst_largelogfile.1.gz | grep "gzip compressed data" \ - > $LTPTMP/tst_logrotate.out 2>&1 || RC=$? - if [ $RC -eq 0 ] - then - tst_resm TPASS \ - "Test #1: logrotate worked as cron, created a compressed file." - else - tst_res TFAIL $LTPTMP/tst_logrotate.out \ - "Test #1: Failed to create a compressed file. Reason:" - fi -else - tst_res TFAIL $LTPTMP/tst_logrotate.out \ - "Test #1: Failed to create /var/log/tst_largelogfile.1.gz. Reason:" - TFAILCNT=$(( $TFAILCNT+1 )) -fi - -} - -# Function: main -# -# Description: - Execute all tests and report results. -# -# Exit: - zero on success -# - non-zero on failure. - -RC=0 -init || exit $? - -test01 || RC=$? - -exit $RC diff --git a/testcases/commands/lsmod/.gitignore b/testcases/commands/lsmod/.gitignore index 0e19fb3f..ba8fca18 100755 --- a/testcases/commands/lsmod/.gitignore +++ b/testcases/commands/lsmod/.gitignore @@ -1,9 +1,6 @@ *.ko *.mod.c -*.ko.cmd -*.mod.cmd -*.mod.o.cmd -*.o.cmd -.built-in.a.cmd +*.cmd Module.symvers modules.order +modules.livepatch diff --git a/testcases/commands/lsmod/lsmod01.sh b/testcases/commands/lsmod/lsmod01.sh index 8b7a0a79..26d2ff83 100755 --- a/testcases/commands/lsmod/lsmod01.sh +++ b/testcases/commands/lsmod/lsmod01.sh @@ -1,6 +1,6 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) Linux Test Project, 2016-2021 +# Copyright (c) Linux Test Project, 2016-2025 # Copyright (c) 2015 Fujitsu Ltd. # Author: Guangwen Feng # @@ -14,6 +14,10 @@ TST_NEEDS_CMDS="lsmod" module_inserted= +# lsmod triggers zcrypt refcount increase if it links against libssl +# which uses hardware acceleration +whitelist_modules='zcrypt' + setup() { if [ -z "$(cat /proc/modules)" ]; then @@ -42,12 +46,15 @@ lsmod_matches_proc_modules() { lsmod_output=$(lsmod \ | awk '!/Module/{print $1, $2, ($3==-2) ? "-" : $3}' \ - | sort) + | sort | grep -v "^$whitelist_modules") + if [ -z "$lsmod_output" ]; then tst_brk TBROK "Failed to parse the output from lsmod" fi - modules_output=$(awk '{print $1, $2, $3}' /proc/modules | sort) + modules_output=$(awk '{print $1, $2, $3}' /proc/modules | sort \ + | grep -v "^$whitelist_modules") + if [ -z "$modules_output" ]; then tst_brk TBROK "Failed to parse /proc/modules" fi diff --git a/testcases/commands/mv/mv_tests.sh b/testcases/commands/mv/mv_tests.sh index 91648dd8..ae8b8701 100755 --- a/testcases/commands/mv/mv_tests.sh +++ b/testcases/commands/mv/mv_tests.sh @@ -88,7 +88,8 @@ test1() mv tst_mv.old tst_mv.new > tst_mv.err 2>&1 if [ $? -ne 0 ]; then cat tst_mv.err - tst_brk TFAIL "Test #1: 'mv tst_mv.old tst_mv.new' failed" + tst_res TFAIL "Test #1: 'mv tst_mv.old tst_mv.new' failed" + return fi tst_res TINFO "Test #1: creating output file" diff --git a/testcases/cve/.gitignore b/testcases/cve/.gitignore index 3a2b2bed..dc1dad5b 100755 --- a/testcases/cve/.gitignore +++ b/testcases/cve/.gitignore @@ -13,3 +13,5 @@ cve-2017-17053 cve-2022-4378 icmp_rate_limit01 tcindex01 +cve-2025-38236 +cve-2025-21756 diff --git a/testcases/cve/Makefile b/testcases/cve/Makefile index c5308794..98c38e90 100755 --- a/testcases/cve/Makefile +++ b/testcases/cve/Makefile @@ -7,7 +7,7 @@ include $(top_srcdir)/include/mk/testcases.mk CFLAGS += -D_GNU_SOURCE -stack_clash: CFLAGS += -fno-optimize-sibling-calls +stack_clash: CFLAGS += -fno-optimize-sibling-calls -Wno-infinite-recursion cve-2016-7042: LDLIBS += $(KEYUTILS_LIBS) @@ -22,6 +22,12 @@ ifneq (,$(filter $(HOST_CPU),x86 x86_64)) meltdown: CFLAGS += -msse2 endif +# The test needs to clobber %rbp, which requires frame pointer omission. Also +# for x86_64, disable AVX since that could sometimes require a stack +# realignment, which gets in the way of frame pointer omission. cve-2015-3290: CFLAGS += -pthread -fomit-frame-pointer +ifeq ($(HOST_CPU),x86_64) +cve-2015-3290: CFLAGS += -mno-avx +endif include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/cve/cve-2014-0196.c b/testcases/cve/cve-2014-0196.c index 9d20a398..d9c4fb7e 100755 --- a/testcases/cve/cve-2014-0196.c +++ b/testcases/cve/cve-2014-0196.c @@ -3,7 +3,11 @@ * Copyright (c) 2017 Richard Palethorpe * Original POC by Matthew Daley */ -/* + +/*\ + * Test for CVE-2014-0196, which was fixed in kernel v3.15: + * 4291086b1f08 ("n_tty: Fix n_tty_write crash when echoing in raw mode"). + * * This test attempts to cause a buffer overflow using the race condition * described in CVE-2014-0196. If the test is successful in causing an * overflow it will most likely result in an immediate Oops, restart or @@ -17,9 +21,8 @@ * have been kept even though they are not strictly necessary to reproduce the * bug. * - * Further details: - * see linux commit 4291086b1f081b869c6d79e5b7441633dc3ace00 - * privilege escalation POC https://www.exploit-db.com/exploits/33516/ + * Further details see privilege escalation POC + * https://www.exploit-db.com/exploits/33516/. */ #include @@ -141,7 +144,7 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .test_all = run, - .max_runtime = 60, + .min_runtime = 60, .tags = (const struct tst_tag[]) { {"linux-git", "4291086b1f08"}, {"CVE", "2014-0196"}, diff --git a/testcases/cve/cve-2015-3290.c b/testcases/cve/cve-2015-3290.c index a2a8fced..cb60582b 100755 --- a/testcases/cve/cve-2015-3290.c +++ b/testcases/cve/cve-2015-3290.c @@ -123,16 +123,14 @@ perhaps unsurprisingly.) #include #include #include -#include #include -#include #include #include #include #include #include -#include "lapi/syscalls.h" +#include "lapi/ldt.h" #include "tst_safe_pthread.h" /* Abstractions for some 32-bit vs 64-bit differences. */ @@ -177,7 +175,11 @@ static greg_t *csptr(ucontext_t *ctx) } #endif +#define LDT_SS 0x7 +#define MAX_FAILS 1000 + static volatile long expected_rsp; +static volatile int fail_count; static int running = 1; static void set_ldt(void) @@ -195,18 +197,19 @@ static void set_ldt(void) .useable = 0 }; - TEST((int)tst_syscall(__NR_modify_ldt, 1, &data_desc, - sizeof(data_desc))); - if (TST_RET == -EINVAL) { - tst_brk(TCONF | TRERRNO, + TEST(modify_ldt(1, &data_desc, sizeof(data_desc))); + if (TST_RET == -1 && TST_ERR == EINVAL) { + tst_brk(TCONF | TTERRNO, "modify_ldt: 16-bit data segments are probably disabled"); } else if (TST_RET != 0) { - tst_brk(TBROK | TRERRNO, "modify_ldt"); + tst_brk(TBROK | TTERRNO, "modify_ldt"); } } -static void try_corrupt_stack(unsigned short orig_ss) +static void try_corrupt_stack(unsigned short *orig_ss) { + unsigned long flags = 0, new_ss = 0; + #ifdef __x86_64__ asm volatile ( /* A small puzzle for the curious reader. */ @@ -214,11 +217,13 @@ static void try_corrupt_stack(unsigned short orig_ss) /* Save rsp for diagnostics */ "mov %%rsp, %[expected_rsp] \n\t" + "xorq %%rax, %%rax \n\t" /* * Let 'er rip. */ - "mov %[ss], %%ss \n\t" /* begin corruption */ + "mov %[ss], %%edx \n\t" + "mov %%edx, %%ss \n\t" /* begin corruption */ "movl $1000, %%edx \n\t" "1: decl %%edx \n\t" "jnz 1b \n\t" @@ -237,20 +242,18 @@ static void try_corrupt_stack(unsigned short orig_ss) * Stop further corruption. We need to check CPL * first because we need RPL == CPL. */ - "mov %[orig_ss], %%ss \n\t" /* end corruption */ + "mov (%[orig_ss]), %%ss \n\t" /* end corruption */ "subq $128, %%rsp \n\t" "pushfq \n\t" - "testl $(1<<9),(%%rsp) \n\t" + "movq (%%rsp),%%rdx \n\t" "addq $136, %%rsp \n\t" - "jz 3f \n\t" - "cmpl %[ss], %%eax \n\t" - "je 4f \n\t" + "jmp 4f \n\t" "3: int3 \n\t" "4: \n\t" - : [expected_rsp] "=m" (expected_rsp) - : [ss] "r" (0x7), [orig_ss] "m" (orig_ss) - : "rax", "rcx", "rdx", "rbp", "r11", "flags" + : [expected_rsp] "=m" (expected_rsp), "+d" (flags), "+a" (new_ss) + : [ss] "n" (LDT_SS), [orig_ss] "r" (orig_ss) + : "rcx", "rbp", "r11", "flags" ); #else asm volatile ( @@ -260,11 +263,13 @@ static void try_corrupt_stack(unsigned short orig_ss) /* Save rsp for diagnostics */ "mov %%esp, %[expected_rsp] \n\t" + "xorl %%eax, %%eax \n\t" /* * Let 'er rip. */ - "mov %[ss], %%ss \n\t" /* begin corruption */ + "mov %[ss], %%edx \n\t" + "mov %%edx, %%ss \n\t" /* begin corruption */ "movl $1000, %%edx \n\t" "1: .byte 0xff, 0xca \n\t" /* decl %edx */ "jnz 1b \n\t" @@ -285,21 +290,30 @@ static void try_corrupt_stack(unsigned short orig_ss) * Stop further corruption. We need to check CPL * first because we need RPL == CPL. */ - "mov %[orig_ss], %%ss \n\t" /* end corruption */ + "mov (%[orig_ss]), %%ss \n\t" /* end corruption */ "pushf \n\t" - "testl $(1<<9),(%%esp) \n\t" + "movl (%%esp), %%edx \n\t" "addl $4, %%esp \n\t" - "jz 3f \n\t" - "cmpl %[ss], %%eax \n\t" - "je 4f \n\t" + "jmp 4f \n\t" "3: int3 \n\t" "4: mov %%esi, %%ebp \n\t" - : [expected_rsp] "=m" (expected_rsp) - : [ss] "r" (0x7), [orig_ss] "m" (orig_ss) - : "eax", "ecx", "edx", "esi", "flags" + : [expected_rsp] "=m" (expected_rsp), "+d" (flags), "+a" (new_ss) + : [ss] "n" (LDT_SS), [orig_ss] "r" (orig_ss) + : "ecx", "esi", "ebp", "flags" ); #endif + + if (!(flags & (1 << 9))) { + tst_res(TFAIL, "Interrupt flag is disabled (0x%lx)", flags); + fail_count++; + } + + if (new_ss != LDT_SS) { + tst_res(TFAIL, "Wrong stack selector 0x%lx, expected 0x%x", + new_ss, LDT_SS); + fail_count++; + } } static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, @@ -315,10 +329,14 @@ static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, static int event_mlock_kb; static int max_sample_rate; -static void *child_thread(void *arg LTP_ATTRIBUTE_UNUSED) +static void *child_thread(void *arg) { + /* + * orig_ss must not be accessed via address relative to %esp, + * otherwise mov %[orig_ss], %%ss above will always segfault + */ + unsigned short *orig_ss = arg; long niter = 0; - unsigned short orig_ss; struct perf_event_attr pe = { .size = sizeof(struct perf_event_attr), @@ -375,7 +393,7 @@ static void *child_thread(void *arg LTP_ATTRIBUTE_UNUSED) SAFE_CLOSE(fd); } - asm volatile ("mov %%ss, %0" : "=rm" (orig_ss)); + asm volatile ("mov %%ss, (%0)" :: "r" (orig_ss)); for (niter = 0; running && niter < 1000*1000*1000L; niter++) { @@ -387,11 +405,17 @@ static void *child_thread(void *arg LTP_ATTRIBUTE_UNUSED) * the system. */ syscall(0x3fffffff); + + if (fail_count >= MAX_FAILS) { + tst_res(TINFO, "Too many failures, exiting"); + running = 0; + break; + } } for (i = 0; i < ARRAY_SIZE(perf_events); i++) if (perf_mmaps[i] != MAP_FAILED) - SAFE_MUNMAP(perf_mmaps[i], 512 * 1024); + SAFE_MUNMAP(perf_mmaps[i], event_mlock_kb * 1024); return (void *)niter; } @@ -401,6 +425,7 @@ static void do_child(void) int i, ncpus; pthread_t *threads; long iter, total_iter = 0; + unsigned short *orig_ss; tst_res(TINFO, "attempting to corrupt nested NMI stack state"); @@ -408,19 +433,28 @@ static void do_child(void) ncpus = tst_ncpus(); threads = SAFE_MALLOC(sizeof(*threads) * ncpus); + orig_ss = SAFE_MALLOC(sizeof(unsigned short) * ncpus); - for (i = 0; i < ncpus; i++) - SAFE_PTHREAD_CREATE(&threads[i], NULL, child_thread, NULL); + for (i = 0; i < ncpus; i++) { + SAFE_PTHREAD_CREATE(&threads[i], NULL, child_thread, + &orig_ss[i]); + } + + while (running && tst_remaining_runtime()) + sleep(1); - sleep(tst_remaining_runtime()); running = 0; for (i = 0; i < ncpus; i++) { SAFE_PTHREAD_JOIN(threads[i], (void **)&iter); total_iter += iter; } + free(orig_ss); free(threads); + if (fail_count) + exit(1); + tst_res(TPASS, "can't corrupt nested NMI state after %ld iterations", total_iter); } @@ -454,10 +488,14 @@ static void run(void) } SAFE_WAITPID(pid, &status, 0); - if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { tst_res(TFAIL, "corrupted NMI stack"); - else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) - tst_res(WEXITSTATUS(status), "Propogate child status"); + } else if (WIFSIGNALED(status)) { + tst_res(TFAIL, "Child killed by unexpected signal %s", + tst_strsig(WTERMSIG(status))); + } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + tst_res(WEXITSTATUS(status), "Propagate child status"); + } } static struct tst_test test = { @@ -465,7 +503,7 @@ static struct tst_test test = { .needs_root = 1, .needs_checkpoints = 1, .setup = setup, - .max_runtime = 180, + .runtime = 180, .test_all = run, .tags = (const struct tst_tag[]) { {"linux-git", "9b6e6a8334d5"}, diff --git a/testcases/cve/cve-2016-10044.c b/testcases/cve/cve-2016-10044.c index 6a8c77f3..d8d3598b 100755 --- a/testcases/cve/cve-2016-10044.c +++ b/testcases/cve/cve-2016-10044.c @@ -3,9 +3,10 @@ * Copyright (c) 2017 Richard Palethorpe * Copyright (c) 2016 Jan Horn */ -/* - * Test for CVE-2016-10044, which was fixed in commit - * 22f6b4d34fcf039c aio: mark AIO pseudo-fs noexec. + +/*\ + * Test for CVE-2016-10044, which was fixed in kernel v4.8: + * 22f6b4d34fcf ("aio: mark AIO pseudo-fs noexec"). * * The test checks that we can not implicitly mark AIO mappings as * executable using the READ_IMPLIES_EXEC personality. diff --git a/testcases/cve/cve-2016-7042.c b/testcases/cve/cve-2016-7042.c index 4434265d..881f891d 100755 --- a/testcases/cve/cve-2016-7042.c +++ b/testcases/cve/cve-2016-7042.c @@ -4,15 +4,10 @@ * Author: Guangwen Feng */ -/* +/*\ * Test for CVE-2016-7042, this regression test can crash the buggy kernel - * when the stack-protector is enabled, and the bug was fixed in: - * - * commit 03dab869b7b239c4e013ec82aea22e181e441cfc - * Author: David Howells - * Date: Wed Oct 26 15:01:54 2016 +0100 - * - * KEYS: Fix short sprintf buffer in /proc/keys show function + * when the stack-protector is enabled, and the bug was fixed in kernel v4.9: + * 03dab869b7b2 ("KEYS: Fix short sprintf buffer in /proc/keys show function"). */ #include diff --git a/testcases/cve/cve-2016-7117.c b/testcases/cve/cve-2016-7117.c index 10933398..d9c44a0b 100755 --- a/testcases/cve/cve-2016-7117.c +++ b/testcases/cve/cve-2016-7117.c @@ -2,13 +2,15 @@ /* * Copyright (c) 2017 Richard Palethorpe */ -/* - * CVE-2016-7117 + +/*\ + * Test for CVE-2016-7117, which was fixed in kernel v4.6: + * 34b88a68f26a ("net: Fix use after free in the recvmmsg exit path"). * - * This tests for a use after free caused by a race between recvmmsg() and - * close(). The exit path for recvmmsg() in (a2e2725541f: net: Introduce - * recvmmsg socket syscall) called fput() on the active file descriptor before - * checking the error state and setting the socket's error field. + * Tests for a use after free caused by a race between recvmmsg() and close(). + * The exit path for recvmmsg() in (a2e2725541f: net: Introduce recvmmsg socket + * syscall) called fput() on the active file descriptor before checking the + * error state and setting the socket's error field. * * If one or more messages are received by recvmmsg() followed by one which * fails, the socket's error field will be set. If just after recvmmsg() calls @@ -149,9 +151,10 @@ static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 60, + .runtime = 60, + .min_runtime = 12, .tags = (const struct tst_tag[]) { - {"linux-git", "a2e2725541fa"}, + {"linux-git", "34b88a68f26a"}, {"CVE", "2016-7117"}, {} } diff --git a/testcases/cve/cve-2017-16939.c b/testcases/cve/cve-2017-16939.c index 18747398..098a8684 100755 --- a/testcases/cve/cve-2017-16939.c +++ b/testcases/cve/cve-2017-16939.c @@ -2,14 +2,15 @@ /* * Copyright (c) 2018 Michael Moese */ -/* Regression test for commit: - * 1137b5e2529a ipsec: Fix aborted xfrm policy dump crash aka CVE-2017-16939 + +/*\ + * Test for CVE-2017-16939, which was fixed in kernel v4.14: + * 1137b5e2529a ("ipsec: Fix aborted xfrm policy dump crash"). * * Based on the reproducing code from Mohammed Ghannam, published on * https://blogs.securiteam.com/index.php/archives/3535 * - * CAUTION! If your system is vulnerable to this CVE, the kernel - * WILL DIE! + * CAUTION! If your system is vulnerable to this CVE, the crashes kernel. */ #include diff --git a/testcases/cve/cve-2017-17052.c b/testcases/cve/cve-2017-17052.c index b9781570..700eb782 100755 --- a/testcases/cve/cve-2017-17052.c +++ b/testcases/cve/cve-2017-17052.c @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Michael Moese + * Based on the reproducer from Eric Biggers. */ -/* - * Test for CVE-2017-17052, original reproducer taken from kernel commit: - * 2b7e8665b4ff51c034c55df3cff76518d1a9ee3a + +/*\ + * Test for CVE-2017-17052, which was fixed in kernel v4.13: + * 2b7e8665b4ff ("fork: fix incorrect fput of ->exe_file causing use-after-free"). * - * CAUTION!! - * This test will crash unpatched kernels! - * Use at your own risk! + * Based on the reproducer taken from fixing kernel commit. * + * CAUTION! If your system is vulnerable to this CVE, the crashes kernel. */ #include diff --git a/testcases/cve/cve-2017-17053.c b/testcases/cve/cve-2017-17053.c index a4c41898..ec7a534a 100755 --- a/testcases/cve/cve-2017-17053.c +++ b/testcases/cve/cve-2017-17053.c @@ -1,28 +1,30 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Michael Moese + * Based on the reproducer from Eric Biggers. */ -/* Regression test for CVE-2017-17053, original reproducer can be found - * here: - * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccd5b3235180eef3cfec337df1c8554ab151b5cc + +/*\ + * Test for CVE-2017-17053, which was fixed in kernel 4.13: + * ccd5b3235180 ("x86/mm: Fix use-after-free of ldt_struct"). * - * Be careful! This test may crash your kernel! + * Based on the reproducer taken from fixing kernel commit. + * + * Test may crash the kernel. */ #include "config.h" #include "tst_test.h" #ifdef HAVE_ASM_LDT_H -#include #include #include #include -#include #include #include #include -#include "lapi/syscalls.h" +#include "lapi/ldt.h" #define EXEC_USEC 5000000 @@ -105,7 +107,7 @@ void run_test(void) struct user_desc desc = { .entry_number = 8191 }; install_sighandler(); - syscall(__NR_modify_ldt, 1, &desc, sizeof(desc)); + SAFE_MODIFY_LDT(1, &desc, sizeof(desc)); for (;;) { if (shm->do_exit) diff --git a/testcases/cve/cve-2017-2618.c b/testcases/cve/cve-2017-2618.c index 4ab3cfb8..98a99841 100755 --- a/testcases/cve/cve-2017-2618.c +++ b/testcases/cve/cve-2017-2618.c @@ -4,15 +4,11 @@ * Author: Guangwen Feng */ -/* - * Test for CVE-2017-2618, this regression test can crash - * the buggy kernel, and the bug was fixed in: +/*\ + * Test for CVE-2017-2618, which was fixed in kernel v4.10: + * 0c461cb727d1 ("selinux: fix off-by-one in setprocattr"). * - * commit 0c461cb727d146c9ef2d3e86214f498b78b7d125 - * Author: Stephen Smalley - * Date: Tue Jan 31 11:54:04 2017 -0500 - * - * selinux: fix off-by-one in setprocattr + * Test may crash the kernel. */ #include diff --git a/testcases/cve/cve-2017-2671.c b/testcases/cve/cve-2017-2671.c index 9092481d..fb30266e 100755 --- a/testcases/cve/cve-2017-2671.c +++ b/testcases/cve/cve-2017-2671.c @@ -3,8 +3,10 @@ * Copyright (c) 2017 Richard Palethorpe * Original POC by Daniel Jiang */ -/* - * Test for CVE-2017-2671 faulty locking on ping socket + +/*\ + * Test for CVE-2017-2671 faulty locking on ping socket fixed in kernel v4.11: + * 43a6684519ab ("ping: implement proper locking"). * * When sys_connect() is called with sockaddr.sin_family set to AF_UNSPEC on a * ping socket; __udp_disconnect() gets called, which in turn calls the buggy @@ -13,8 +15,6 @@ * underneath it in the time between calling sk_hashed() and gaining the write * lock. * - * Fixed in commit 43a6684519ab0a6c52024b5e25322476cabad893 - * * This test repeatedly 'connects' a ping socket correctly then calls * connect() with AF_UNSPEC in two seperate threads to trigger the race * condition. If the bug is present, then the test will most likely crash the @@ -109,7 +109,8 @@ static struct tst_test test = { .test_all = run, .cleanup = cleanup, .needs_root = 1, - .max_runtime = 40, + .runtime = 40, + .min_runtime = 2, .tags = (const struct tst_tag[]) { {"linux-git", "43a6684519ab"}, {"CVE", "2017-2671"}, diff --git a/testcases/cve/cve-2022-4378.c b/testcases/cve/cve-2022-4378.c index 402fa333..e7e2b9af 100644 --- a/testcases/cve/cve-2022-4378.c +++ b/testcases/cve/cve-2022-4378.c @@ -4,16 +4,11 @@ */ /*\ - * CVE 2022-4378 + * Test for CVE-2022-4378 fixed in kernel v6.1: + * bce9332220bd ("proc: proc_skip_spaces() shouldn't think it is working on C strings"). * * Check that writing several pages worth of whitespace into /proc/sys files - * does not cause kernel stack overflow. Kernel bug fixed in: - * - * commit bce9332220bd677d83b19d21502776ad555a0e73 - * Author: Linus Torvalds - * Date: Mon Dec 5 12:09:06 2022 -0800 - * - * proc: proc_skip_spaces() shouldn't think it is working on C strings + * does not cause kernel stack overflow. */ #include diff --git a/testcases/cve/cve-2025-21756.c b/testcases/cve/cve-2025-21756.c new file mode 100644 index 00000000..80fb84c4 --- /dev/null +++ b/testcases/cve/cve-2025-21756.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Test for CVE-2025-21756 fixed in kernel v6.14: + * fcdd2242c023 vsock: Keep the binding until socket destruction + * + * Reproducer based on: + * https://lore.kernel.org/all/20250128-vsock-transport-vs-autobind-v3-5-1cf57065b770@rbox.co/ + * + * Beware, this test will crash the system. + */ + +#include "tst_test.h" +#include "lapi/vm_sockets.h" + +#define MAX_PORT_RETRIES 24 +#define VMADDR_CID_NONEXISTING 42 + +static int vsock_bind(unsigned int cid, unsigned int port, int type) +{ + int sock; + + struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_cid = cid, + .svm_port = port, + }; + + sock = SAFE_SOCKET(AF_VSOCK, type, 0); + + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1) { + if (errno == EINVAL) + tst_brk(TCONF, "VM socket is not supported"); + + tst_brk(TBROK | TERRNO, "bind() error"); + } + + return sock; +} + +static void run(void) +{ + int sockets[MAX_PORT_RETRIES]; + struct sockaddr_vm addr; + int socket, sock_count; + socklen_t alen; + + socket = vsock_bind(VMADDR_CID_LOCAL, VMADDR_PORT_ANY, SOCK_SEQPACKET); + + alen = sizeof(addr); + SAFE_GETSOCKNAME(socket, (struct sockaddr *)&addr, &alen); + + for (sock_count = 0; sock_count < MAX_PORT_RETRIES; ++sock_count) { + sockets[sock_count] = vsock_bind(VMADDR_CID_ANY, + ++addr.svm_port, SOCK_SEQPACKET); + } + + SAFE_CLOSE(socket); + + socket = SAFE_SOCKET(AF_VSOCK, SOCK_SEQPACKET, 0); + if (!connect(socket, (struct sockaddr *)&addr, alen)) + tst_brk(TBROK, "Unexpected connect() #1 success"); + + addr.svm_cid = VMADDR_CID_NONEXISTING; + if (!connect(socket, (struct sockaddr *)&addr, alen)) + tst_brk(TBROK, "Unexpected connect() #2 success"); + + addr.svm_cid = VMADDR_CID_LOCAL; + addr.svm_port = VMADDR_PORT_ANY; + + /* Vulnerable system may crash now. */ + SAFE_BIND(socket, (struct sockaddr *)&addr, alen); + SAFE_CLOSE(socket); + + if (tst_taint_check()) + tst_res(TFAIL, "Kernel is vulnerable"); + else + tst_res(TPASS, "Kernel survived after socket unbinding"); + + while (sock_count--) + SAFE_CLOSE(sockets[sock_count]); +} + +static struct tst_test test = { + .test_all = run, + .taint_check = TST_TAINT_W | TST_TAINT_D, + .tags = (const struct tst_tag[]) { + {"linux-git", "fcdd2242c023"}, + {"CVE", "2025-21756"}, + {} + }, +}; diff --git a/testcases/cve/cve-2025-38236.c b/testcases/cve/cve-2025-38236.c new file mode 100644 index 00000000..16582fe6 --- /dev/null +++ b/testcases/cve/cve-2025-38236.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Test for CVE-2025-38236 fixed in kernel v6.16-rc4: + * 32ca245464e1 ("af_unix: Don't leave consecutive consumed OOB skbs"). + * + * The bug is triggered by sending multiple out-of-band data to a socket and + * reading it back from it. According to the MSG_OOB implementation, this + * shouldn't be possible. When system is affected by CVE-2025-38236, instead, + * skb queue holds MSG_OOB data, breaking recv() and causing a use-after-free + * condition. + * + * Even if MSG_OOB is mostly used inside Oracle's product, it is enabled by + * default in linux kernel via CONFIG_AF_UNIX_OOB. This is accessible via + * Chrome's renderer sandbox, which might cause an attacker to escalate and to + * obtain privileges in the system. + * + * Reproducer is based on: + * https://project-zero.issues.chromium.org/issues/423023990 + */ + +#include "tst_test.h" + +static char dummy; +static int sock[2]; + +static void run(void) +{ + int ret; + + dummy = '\0'; + + tst_res(TINFO, "#1 send and receive out-of-band data"); + SAFE_SEND(0, sock[1], "A", 1, MSG_OOB); + SAFE_RECV(0, sock[0], &dummy, 1, MSG_OOB); + + tst_res(TINFO, "#2 send and receive out-of-band data"); + SAFE_SEND(0, sock[1], "B", 1, MSG_OOB); + SAFE_RECV(0, sock[0], &dummy, 1, MSG_OOB); + + tst_res(TINFO, "Send out-of-band data"); + SAFE_SEND(0, sock[1], "C", 1, MSG_OOB); + + tst_res(TINFO, "Receive data from normal stream"); + + ret = recv(sock[0], &dummy, 1, MSG_DONTWAIT); + if (ret == -1) { + if (errno == EWOULDBLOCK) { + tst_res(TPASS, "Can't read out-of-band data from normal stream"); + return; + } + + tst_brk(TBROK | TERRNO, "recv error"); + } + + const char *msg = "We are able to read out-of-band data from normal stream"; + + if (dummy == 'C') { + tst_res(TFAIL, "%s", msg); + } else { + tst_res(TFAIL, "%s, but data doesn't match: '%c' != 'A'", + msg, dummy); + } + + SAFE_RECV(0, sock[0], &dummy, 1, MSG_OOB); + + tst_res(TFAIL, "We are able to access data from skb queue (use-after-free)"); +} + +static void setup(void) +{ + SAFE_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, sock); +} + +static void cleanup(void) +{ + if (sock[0] != -1) + SAFE_CLOSE(sock[0]); + + if (sock[1] != -1) + SAFE_CLOSE(sock[1]); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_kconfigs = (const char *[]) { + "CONFIG_AF_UNIX_OOB=y", + NULL + }, + .tags = (const struct tst_tag[]) { + {"linux-git", "32ca245464e1"}, + {"CVE", "2025-38236"}, + {} + } +}; diff --git a/testcases/cve/icmp_rate_limit01.c b/testcases/cve/icmp_rate_limit01.c index 8ee50a27..0305ad09 100755 --- a/testcases/cve/icmp_rate_limit01.c +++ b/testcases/cve/icmp_rate_limit01.c @@ -6,21 +6,14 @@ */ /*\ - * CVE-2020-25705 + * Test for CVE-2020-25705 fixed in kernel v5.10: + * b38e7819cae9 ("icmp: randomize the global rate limiter"). * * Test of ICMP rate limiting behavior that may be abused for DNS cache * poisoning attack. Send a few batches of 100 packets to a closed UDP port * and count the ICMP errors. If the number of errors is always the same * for each batch (not randomized), the system is vulnerable. Send packets * from multiple IP addresses to bypass per-address ICMP throttling. - * - * Fixed in: - * - * commit b38e7819cae946e2edf869e604af1e65a5d241c5 - * Author: Eric Dumazet - * Date: Thu Oct 15 11:42:00 2020 -0700 - * - * icmp: randomize the global rate limiter */ #include diff --git a/testcases/cve/meltdown.c b/testcases/cve/meltdown.c index 398e496a..d2c40c12 100755 --- a/testcases/cve/meltdown.c +++ b/testcases/cve/meltdown.c @@ -3,6 +3,12 @@ * Copyright (c) 2018 Pavel Boldin */ +/*\ + * Test for CVE-2017-5754 (Meltdown). + * + * https://meltdownattack.com/ + */ + #include "config.h" #include "tst_test.h" diff --git a/testcases/cve/stack_clash.c b/testcases/cve/stack_clash.c index 3a99c49b..19da449d 100755 --- a/testcases/cve/stack_clash.c +++ b/testcases/cve/stack_clash.c @@ -6,22 +6,22 @@ */ /*\ - * [Description] + * This is a regression test of the `Stack Clash + * `_ + * vulnerability. This tests that there is at least 256 PAGE_SIZE of stack guard + * gap which is considered hard to hop above. Code is based on a reproducer from + * https://bugzilla.suse.com/show_bug.cgi?id=CVE-2017-1000364. * - * This is a regression test of the Stack Clash [1] vulnerability. This tests - * that there is at least 256 PAGE_SIZE of stack guard gap which is considered - * hard to hop above. Code adapted from the Novell's bugzilla [2]. - * - * The code `mmap(2)`s region close to the stack end. The code then allocates + * The code :man2:`mmap` region close to the stack end. The code then allocates * memory on stack until it hits guard page and SIGSEGV or SIGBUS is generated * by the kernel. The signal handler checks that fault address is further than * THRESHOLD from the mmapped area. * - * We read /proc/self/maps to examine exact top of the stack and `mmap(2)` + * We read /proc/self/maps to examine exact top of the stack and :man2:`mmap` * our region exactly GAP_PAGES * PAGE_SIZE away. We read /proc/cmdline to * see if a different stack_guard_gap size is configured. We set stack limit * to infinity and preallocate REQ_STACK_SIZE bytes of stack so that no calls - * after `mmap` are moving stack further. + * after mmap() are moving stack further. * * If the architecture meets certain requirements (only x86_64 is verified) * then the test also tests that new mmap()s can't be placed in the stack's @@ -29,11 +29,8 @@ * assumptions are that the stack grows down (start gap) and either: * * 1. The default search is top down, and will switch to bottom up if - * space is exhausted. + * space is exhausted. * 2. The default search is bottom up and the stack is above mmap base. - * - * [1] https://blog.qualys.com/securitylabs/2017/06/19/the-stack-clash - * [2] https://bugzilla.novell.com/show_bug.cgi?id=CVE-2017-1000364 */ #include @@ -44,6 +41,7 @@ #include #include "tst_test.h" +#include "tst_kconfig.h" #include "tst_safe_stdio.h" #include "lapi/mmap.h" @@ -92,6 +90,7 @@ void segv_handler(int sig, siginfo_t *info, void *data LTP_ATTRIBUTE_UNUSED) _exit(EXIT_SUCCESS); } +#ifdef __x86_64__ static void force_bottom_up(void) { FILE *fh; @@ -134,6 +133,7 @@ static void force_bottom_up(void) out: SAFE_FCLOSE(fh); } +#endif unsigned long read_stack_addr_from_proc(unsigned long *stack_size) { @@ -187,6 +187,7 @@ void __attribute__((noinline)) preallocate_stack(unsigned long required) garbage[0] = garbage[required - 1] = '\0'; } +#ifdef __x86_64__ static void do_mmap_placement_test(unsigned long stack_addr, unsigned long gap) { void *map_test_gap; @@ -208,6 +209,7 @@ static void do_mmap_placement_test(unsigned long stack_addr, unsigned long gap) SAFE_MUNMAP(map_test_gap, MAPPED_LEN); } } +#endif void do_child(void) { @@ -268,19 +270,14 @@ void do_child(void) void setup(void) { - char buf[4096], *p; - page_size = sysconf(_SC_PAGESIZE); page_mask = ~(page_size - 1); - buf[4095] = '\0'; - SAFE_FILE_SCANF("/proc/cmdline", "%4095[^\n]", buf); + struct tst_kcmdline_var params = TST_KCMDLINE_INIT("stack_guard_gap"); + tst_kcmdline_parse(¶ms, 1); - if ((p = strstr(buf, "stack_guard_gap=")) != NULL) { - if (sscanf(p, "stack_guard_gap=%ld", &GAP_PAGES) != 1) { - tst_brk(TBROK | TERRNO, "sscanf"); - return; - } + if (params.found) { + GAP_PAGES= atol(params.value); tst_res(TINFO, "stack_guard_gap = %ld", GAP_PAGES); } diff --git a/testcases/cve/tcindex01.c b/testcases/cve/tcindex01.c index 91bfafb5..370d7de4 100644 --- a/testcases/cve/tcindex01.c +++ b/testcases/cve/tcindex01.c @@ -11,20 +11,15 @@ * Test for use-after-free after removing tcindex traffic filter with certain * parameters. * - * Tcindex filter removed in: - * - * commit 8c710f75256bb3cf05ac7b1672c82b92c43f3d28 - * Author: Jamal Hadi Salim - * Date: Tue Feb 14 08:49:14 2023 -0500 - * - * net/sched: Retire tcindex classifier + * Tcindex filter was removed in kernel v6.3: + * 8c710f75256b ("net/sched: Retire tcindex classifier") */ #include #include #include #include "tst_test.h" -#include "tst_rtnetlink.h" +#include "tst_netlink.h" #include "tst_netdevice.h" #include "lapi/sched.h" #include "lapi/if_ether.h" @@ -32,6 +27,23 @@ #define DEVNAME "ltp_dummy1" +#ifndef TCA_TCINDEX_MAX +enum { + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) +#endif + + static const uint32_t qd_handle = TC_H_MAKE(1 << 16, 0); static const uint32_t clsid = TC_H_MAKE(1 << 16, 1); static const uint32_t shift = 10; @@ -46,15 +58,15 @@ static const struct tc_htb_glob qd_opt = { static struct tc_htb_opt cls_opt = {}; /* htb qdisc and class options */ -static const struct tst_rtnl_attr_list qd_config[] = { - {TCA_OPTIONS, NULL, 0, (const struct tst_rtnl_attr_list[]){ +static const struct tst_netlink_attr_list qd_config[] = { + {TCA_OPTIONS, NULL, 0, (const struct tst_netlink_attr_list[]){ {TCA_HTB_INIT, &qd_opt, sizeof(qd_opt), NULL}, {0, NULL, -1, NULL} }}, {0, NULL, -1, NULL} }; -static const struct tst_rtnl_attr_list cls_config[] = { - {TCA_OPTIONS, NULL, 0, (const struct tst_rtnl_attr_list[]){ +static const struct tst_netlink_attr_list cls_config[] = { + {TCA_OPTIONS, NULL, 0, (const struct tst_netlink_attr_list[]){ {TCA_HTB_PARMS, &cls_opt, sizeof(cls_opt), NULL}, {0, NULL, -1, NULL} }}, @@ -62,8 +74,8 @@ static const struct tst_rtnl_attr_list cls_config[] = { }; /* tcindex filter options */ -static const struct tst_rtnl_attr_list f_config[] = { - {TCA_OPTIONS, NULL, 0, (const struct tst_rtnl_attr_list[]){ +static const struct tst_netlink_attr_list f_config[] = { + {TCA_OPTIONS, NULL, 0, (const struct tst_netlink_attr_list[]){ {TCA_TCINDEX_MASK, &mask, sizeof(mask), NULL}, {TCA_TCINDEX_SHIFT, &shift, sizeof(shift), NULL}, {TCA_TCINDEX_CLASSID, &clsid, sizeof(clsid), NULL}, @@ -89,13 +101,24 @@ static void run(void) NETDEV_ADD_QDISC(DEVNAME, AF_UNSPEC, TC_H_ROOT, qd_handle, "htb", qd_config); NETDEV_ADD_TRAFFIC_CLASS(DEVNAME, qd_handle, clsid, "htb", cls_config); - NETDEV_ADD_TRAFFIC_FILTER(DEVNAME, qd_handle, 10, ETH_P_IP, 1, - "tcindex", f_config); + ret = NETDEV_ADD_TRAFFIC_FILTER_RET(DEVNAME, qd_handle, 10, ETH_P_IP, + 1, "tcindex", f_config); + TST_ERR = tst_netlink_errno; + + if (!ret && TST_ERR == ENOENT) { + tst_res(TPASS | TTERRNO, + "tcindex module is blacklisted or unavailable"); + return; + } + + if (!ret) + tst_brk(TBROK | TTERRNO, "Cannot add tcindex filter"); + NETDEV_REMOVE_TRAFFIC_FILTER(DEVNAME, qd_handle, 10, ETH_P_IP, 1, "tcindex"); - ret = tst_netdev_add_traffic_filter(__FILE__, __LINE__, 0, DEVNAME, - qd_handle, 10, ETH_P_IP, 1, "tcindex", f_config); - TST_ERR = tst_rtnl_errno; + ret = NETDEV_ADD_TRAFFIC_FILTER_RET(DEVNAME, qd_handle, 10, ETH_P_IP, + 1, "tcindex", f_config); + TST_ERR = tst_netlink_errno; NETDEV_REMOVE_QDISC(DEVNAME, AF_UNSPEC, TC_H_ROOT, qd_handle, "htb"); if (ret) @@ -128,6 +151,10 @@ static struct tst_test test = { {"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP}, {} }, + .needs_drivers = (const char *const []) { + "dummy", + NULL + }, .tags = (const struct tst_tag[]) { {"linux-git", "8c710f75256b"}, {"CVE", "2023-1829"}, diff --git a/testcases/kdump/Makefile b/testcases/kdump/Makefile deleted file mode 100755 index 28fdf64d..00000000 --- a/testcases/kdump/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -all: runkdump.conf - chmod a+x runkdump.sh lib/*.sh - ./runkdump.sh diff --git a/testcases/kdump/README b/testcases/kdump/README deleted file mode 100755 index e895eb84..00000000 --- a/testcases/kdump/README +++ /dev/null @@ -1,90 +0,0 @@ -KDUMP TESTS AUTOMATION SUITE ----------------------------- - -The kdump test automation suite helps run the kdump tests and report -results. The testscripts cycle through a series of crash -scenarios. Each test cycle does the following: - -1. Sets up a crash scenario. -2. Forces a crash. -3. Kdump kernel boots and saves a vmcore. -4. System reboots to 1st kernel. -5. vmcore is validated and results are saved. -6. After a 1 to 2 minute delay, the next crash scenario is setup and - run. - -The scripts make use of the crasher module for basic testing of kdump -and the new Linux Kernel Dump Test Module (LKDTM) for more involved -testing. LKDTM makes use of the kprobes infrastructure for inserting -crashpoints into the kernel at run-time. Thus the kernel need not be -patched and rebuilt.This script also tests kdump dumping on different -destinations like ext3, raw and network dump. - -KDUMP TEST INSTRUCTION ----------------------- - -Follow the steps to setup kdump test automation suite. - -The tests are written for SuSE Linux Enterprise Server 10 (and onward -releases), OpenSUSE, Fedora, Debian, as well as RedHat Enterprise Linux -5. Since KDUMP is supported by the above mentioned distro's the tests -were written and tested on them. Contribution towards supporting more -distributions are welcome. - -1. Install these additional packages: - -For SLES10 or OpenSUSE Distro : - - * kernel-kdump - * kernel-source - * kexec-tools - * zlib-64bit- (ppc64 only) - * expect (if dump to a network destination) - -For RHEL5 or Fedora distro : - - * kexec-tools - * kernel-devel - * kernel-debuginfo rpm (if using crash to verify vmcore) - * kernel-kdump ( only for ppc64 ) - * expect (if dump to a network destination) - -2. Make sure the partition where the tests are running has space for -the tests results and one vmcore file (size of physical memory). - -3. Modify configuration file runkdump.conf or copy a existing one from -sample/. - -4. Run "make". Carefully check for any errors. - -Few Important points to remember: - -* If you need to stop the tests before all tests have run, run "crontab --r" and "killall runkdump.sh" within 1 minute after the 1st kernel -reboots. Then, if you'd like to carry on tests from the point on, run -"crontab kdump.cron", and "./runkdump.sh". If you'd like to start tests -from the beginning, modify the configuration file, and set -"REBOOT=0". Then, "./runkdump.sh" - -* A failure is likely to occur when booting the kdump kernel. If this -happens, you'll need to manually reset the system so it reboots back to -the 1st kernel and continues on to the next test. For this reason, it's -best to monitor the tests from a console. If possible, setup a serial -console (not a must, any type of console setup will do). If using -minicom, enable saving of kernel messages displayed on minicom into a -file, by pressing ctrl+a+l on the console. Else, when it is observed -that the kdump kernel has failed to boot, manually copy the boot message -into a file to enable the debugging the cause of the hang. - -* The results are saved in -/log/.. The "status" file in that -directory shows where you are in the test run. When the "Test run -complete" entry appears in that file, you're done. Verbose log can be -found at /tmp/kdump-.log. - -* The test machine would be unavailable for any other work during the -period of the test run. - -* System may hang if incorrect partition information is provided for -dumping, like specifying a partition which does not exist, specifying a -partition label which does not exist. This is not ltp kdump bug. diff --git a/testcases/kdump/doc/00_Descriptions.txt b/testcases/kdump/doc/00_Descriptions.txt deleted file mode 100755 index c2d497d0..00000000 --- a/testcases/kdump/doc/00_Descriptions.txt +++ /dev/null @@ -1,71 +0,0 @@ -Kdump Test Description ----------------------- - -The test suite consists of two parts - - * crasher: This is a basic kdump functionality testing. - * lkdtm : These tests use kprobes / jprobes to put probes in different - function for invoking a dump. - -Only one of the above is used for testing at a time. While building the scripts, -select the one you want to run. It is suggested to use the lkdtm tests (which is -the default) for more elaborate testing. - -The crasher module tests kdump in following senarios. - - * ACS: invoke dump using sysrq C. - * ACP: invoke dump using panic [ panic test 0 ]. - * ACB: invoke dump using BUG [ panic test 1 ]. - * ACE: invoke dump using panic_on_oops [ panic test 2 ]. - * ACL: invoke dump in a hang situation. - -On some type of hardware [ eg IBM System p and System i machines ] tests ACB -and ACE will require manual intervention. After running these tests machine -will drop into xmon. User will have to type X to get out of the xmon and -continue with the tests. As for the ACL test, user will have to invoke a dump -using the Hardware Management Console via soft reset. - -The lkdtm module consists of following tests - - * KPIDB: bug in do_irq - * KPIDE: exception in do_irq - * KPIDL: hang in do_irq - * KPIDP: panic in do_irq - * KPIEB: bug in handle_IRQ_event - * KPIEE: exception in handle_IRQ_event - * KPIEL: hang in handle_IRQ_event - * KPIEP: panic in handle_IRQ_event - * KPTEB: bug in tasklet_action - * KPTEE: exception in tasklet_action - * KPTEL: hang in tasklet_action - * KPTEP: panic in tasklet_action - * KPBB : bug in ll_rw_block - * KPBE : exception in ll_rw_block - * KPBL : hang in ll_rw_block - * KPBP : panic in ll_rw_block - * KPMSB: bug in shrink_inactive_list - * KPMSE: exception in shrink_inactive_list - * KPMSL: hang in shrink_inactive_list - * KPMSP: panic in shrink_inactive_list - * KPTB : bug in hr_timer_start - * KPTE : exception in hr_timer_start - * KPTL : hang in hr_timer_start - * KPTP : panic in hr_timer_start - -On certain type of hardware [ eg IBM system p and system i machines] tests with -names ending in "B" and "E" might require manual intervention. After running -these tests machine will drop into debugger if one is configured. User will -have to type X to get out of the debugger and continue with the tests. For -tests with name ending in L [loop test] , user will have to invoke a dump using -the Hardware Management Console via soft reset. - -While executing some loop testcases after invoking a dump via Hardware -Management Console you might get following message - -Sending IPI to other cpus... -done waiting: 1 cpu(s) not responding -Activate soft-reset to stop other cpu(s) - -In such case you will have to re-trigger dump via Hardware Management Console -one more time. This is a normal senario and not a bug. - diff --git a/testcases/kdump/doc/ALL_TEST.txt b/testcases/kdump/doc/ALL_TEST.txt deleted file mode 100755 index fa91c0e6..00000000 --- a/testcases/kdump/doc/ALL_TEST.txt +++ /dev/null @@ -1,57 +0,0 @@ -The crasher module tests kdump in following scenarios. - * ACS: invoke dump using sysrq C. - * ACP: invoke dump using panic [ panic test 0 ]. - * ACB: invoke dump using BUG [ panic test 1 ]. - * ACE: invoke dump using panic_on_oops [ panic test 2 ]. - * ACL: invoke dump in a hang situation. - -The LKDTM (Linux Kernel Dump Test Module) module consists of following tests, - * KPIDB: bug in do_irq - * KPIDE: exception in do_irq - * KPIDL: hang in do_irq - * KPIDP: panic in do_irq - * KPIDO: overflow in do_irq - * KPIEB: bug in handle_IRQ_event - * KPIEE: exception in handle_IRQ_event - * KPIEL: hang in handle_IRQ_event - * KPIEP: panic in handle_IRQ_event - * KPIEO: overflow in handle_IRQ_event - * KPTEB: bug in tasklet_action - * KPTEE: exception in tasklet_action - * KPTEL: hang in tasklet_action - * KPTEP: panic in tasklet_action - * KPTEO: overflow in tasklet_action - * KPBB : bug in ll_rw_block - * KPBE : exception in ll_rw_block - * KPBL : hang in ll_rw_block - * KPBP : panic in ll_rw_block - * KPBO : overflow in ll_rw_block - * KPMSB: bug in shrink_inactive_list - * KPMSE: exception in shrink_inactive_list - * KPMSL: hang in shrink_inactive_list - * KPMSP: panic in shrink_inactive_list - * KPMSO: overflow in shrink_inactive_list - * KPTB : bug in hr_timer_start - * KPTE : exception in hr_timer_start - * KPTL : hang in hr_timer_start - * KPTP : panic in hr_timer_start - * KPTO : overflow in hr_timer_start - * KPSB : bug in scsi_dispatch_cmd - * KPSE : exception in scsi_dispatch_cmd - * KPSL : hang in scsi_dispatch_cmd - * KPSP : panic in scsi_dispatch_cmd - * KPSO : overflow in scsi_dispatch_cmd - * KPIB : bug in ide_core_cp - * KPIE : exception in ide_core_cp - * KPIL : hang in ide_core_cp - * KPIP : panic in ide_core_cp - * KPIO : overflow in ide_core_cp - -Extra tests include dump on different destinations, - * KLEXT: dump on an EXT3 partition - * KLLBL: dump on an EXT3 partition with a LABEL - * KLUID: dump on an EXT3 partition with a partition UID. - * KLRAW: dump on a RAW partition. - * KNSCP: dump on network. - * KNNFS: dump on a mounted NFS filesystem. - * KDENB: kdump propagate diff --git a/testcases/kdump/doc/README b/testcases/kdump/doc/README deleted file mode 100755 index d2ef1633..00000000 --- a/testcases/kdump/doc/README +++ /dev/null @@ -1,90 +0,0 @@ -KDUMP TESTS AUTOMATION SUITE ----------------------------- - -The kdump test automation suite helps run the kdump tests and report -results. The testscripts cycle through a series of crash -scenarios. Each test cycle does the following: - -1. Sets up a crash scenario. -2. Forces a crash. -3. Kdump kernel boots and saves a vmcore. -4. System reboots to 1st kernel. -5. vmcore is validated and results are saved. -6. After a 1 to 2 minute delay, the next crash scenario is setup and - run. - -The scripts make use of the crasher module for basic testing of kdump -and the new Linux Kernel Dump Test Module (LKDTM) for more involved -testing. LKDTM makes use of the kprobes infrastructure for inserting -crashpoints into the kernel at run-time. Thus the kernel need not be -patched and rebuilt.This script also tests kdump dumping on different -destinations like ext3, raw and network dump. - -KDUMP TEST INSTRUCTION ----------------------- - -Follow the steps to setup kdump test automation suite. - -The tests are written for SuSE Linux Enterprise Server 10 (and onward -releases), OpenSUSE, Fedora, Debian, as well as RedHat Enterprise Linux -5. Since KDUMP is supported by the above mentioned distro's the tests -were written and tested on them. Contribution towards supporting more -distributions are welcome. - -1. Install these additional packages: - -For SLES10 or OpenSUSE Distro : - - * kernel-kdump - * kernel-source - * kexec-tools - * zlib-64bit- (ppc64 only) - * expect (if dump to a network destination) - -For RHEL5 or Fedora distro : - - * kexec-tools - * kernel-devel - * kernel-debuginfo rpm (if using crash to verify vmcore) - * kernel-kdump ( only for ppc64 ) - * expect (if dump to a network destination) - -2. Make sure the partition where the tests are running has space for -the tests results and one vmcore file (size of physical memory). - -3. Modify configuration file runkdump.conf or copy a existing one from -sample/. - -4. Run "runkdump.sh". Carefully check for any errors. - -Few Important points to remember: - -* If you need to stop the tests before all tests have run, run "crontab --r" and "killall runkdump.sh" within 1 minute after the 1st kernel -reboots. Then, if you'd like to carry on tests from the point on, run -"crontab kdump.cron", and "./runkdump.sh". If you'd like to start tests -from the beginning, modify the configuration file, and set -"REBOOT=0". Then, "./runkdump.sh" - -* A failure is likely to occur when booting the kdump kernel. If this -happens, you'll need to manually reset the system so it reboots back to -the 1st kernel and continues on to the next test. For this reason, it's -best to monitor the tests from a console. If possible, setup a serial -console (not a must, any type of console setup will do). If using -minicom, enable saving of kernel messages displayed on minicom into a -file, by pressing ctrl+a+l on the console. Else, when it is observed -that the kdump kernel has failed to boot, manually copy the boot message -into a file to enable the debugging the cause of the hang. - -* The results are saved in -/log/.. The "status" file in that -directory shows where you are in the test run. When the "Test run -complete" entry appears in that file, you're done. Verbose log can be -found at /tmp/kdump.log. - -* The test machine would be unavailable for any other work during the -period of the test run. - -* System may hang if incorrect partition information is provided for -dumping, like specifying a partition which does not exist, specifying a -partition label which does not exist. This is not ltp kdump bug. diff --git a/testcases/kdump/doc/TEST_PLAN.txt b/testcases/kdump/doc/TEST_PLAN.txt deleted file mode 100755 index 3fe24399..00000000 --- a/testcases/kdump/doc/TEST_PLAN.txt +++ /dev/null @@ -1,49 +0,0 @@ -Following is the Tentative plan for improving LTP-KDUMP Test-cases: - -=========================================================================================================== -||S.NO|| ACTIVITY || TENTATIVE-TIME || -=========================================================================================================== -|| 1 || Enhance result analysis code of Link Delay || JAN 2008 || -|| || and dump filtering || || -|| 2 || Develop failure recovery code for failures which || JAN 2008 || -|| || could be identified and fixed || || -|| 3 || Enhance ltp kdump to support kdump testing || DEC 2007 || -|| || on open suse and fedora || || -=========================================================================================================== - -Proposed upcoming work from Cai Qian : - -Here is my first draft plan of Kexec/Kdump tests enhancement sorted by -priorities. I would like to add them as many as possible. - -== filtered vmcore utilities == -- in different compressed levels, verify the vmcore with the correct - layout. -- verify it in flat file or ELF formats from a network host. - -== analyse vmcore utilities == -- GDB -- crash with better error detecting. -- crash to analyse Hypervisor and Dom0 Kernel. - -== test scripts == -- timestamp information for crash was triggered, vmcore was generated, - and vmcore was verified. -- aim to 100% automation, and reduce manual setup. -- tidy up scripts. - -== crash scenarios == -- SDINT switch for ia64 if possible. -- Hypervisor crash for Virtualization. -- crashes on full- and para-virt guests. - -== fix bugs in existing tests == -- printk LKDTM module can hang the second Kernel. - -== kdump configurations and init script == -- capture vmcore after init runs. -- rpm pre- and post-scripts -- kdump_pre and kdump_post directives - -== increase coverages for new kexec/kdump development efforts == -- new reserved region syntax in Kernel. diff --git a/testcases/kdump/lib/Makefile b/testcases/kdump/lib/Makefile deleted file mode 100755 index 547a5242..00000000 --- a/testcases/kdump/lib/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -default: - $(MAKE) -C crasher - $(MAKE) -C lkdtm - - -clean: - $(MAKE) -C crasher clean - $(MAKE) -C lkdtm clean diff --git a/testcases/kdump/lib/crasher/Makefile b/testcases/kdump/lib/crasher/Makefile deleted file mode 100755 index 728cfeb8..00000000 --- a/testcases/kdump/lib/crasher/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -obj-m := crasher.o -KDIR := /lib/modules/$(shell uname -r)/build -PWD := $(shell pwd) - -default: - $(MAKE) -C $(KDIR) M=$(PWD) modules - - -clean: - make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean - rm -f Module.symvers -#clean: -# rm -f *.mod.c *.ko *.o .*.cmd *.symvers -# rm -rf .tmp_versions diff --git a/testcases/kdump/lib/crasher/crasher.c b/testcases/kdump/lib/crasher/crasher.c deleted file mode 100755 index fcdf53a2..00000000 --- a/testcases/kdump/lib/crasher/crasher.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Crasher module for kdump testing - * - * 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. - * - * Copyright © IBM Corporation 2007 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); - -int crasher_init(void); -void crasher_exit(void); -module_init(crasher_init); -module_exit(crasher_exit); - -#define CRASH "crasher" /* name of /proc entry file */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) -static int crasher_read(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -#else -static ssize_t crasher_read(struct file *file, char __user *buf, size_t len, - loff_t *offset) -#endif -{ - return (sprintf(buf, "\n")); -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) -static int crasher_write(struct file *file, const char *buffer, - unsigned long count, void *data) -#else -static ssize_t crasher_write(struct file *file, const char __user *buffer, - size_t count, loff_t *data) -#endif -{ - char value, *a; - DEFINE_SPINLOCK(mylock); - - /* grab the first byte the user gave us, ignore the rest */ - if (copy_from_user(&value, buffer, 1)) - return -EFAULT; - - switch (value) { - case '0': /* panic the system */ - panic("KDUMP test panic\n"); - break; - - case '1': /* BUG() test */ - BUG(); - break; - - case '2': /* panic_on_oops test */ - a = 0; - a[1] = 'A'; - break; - - case '3': /* hang w/double spinlock */ - spin_lock_irq(&mylock); - spin_lock_irq(&mylock); - break; - - default: - printk("crasher: Bad command\n"); - } - - return count; /* tell the user we read all his data, - somtimes white lies are ok */ -} - -/* create a directory in /proc and a debug file in the new directory */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) -static const struct file_operations crasher_proc_fops = { - .owner = THIS_MODULE, - .read = crasher_read, - .write = crasher_write, -}; -#endif - -int crasher_init(void) -{ - struct proc_dir_entry *crasher_proc; - - printk("loaded crasher module\n"); - - /* build a crasher file that can be set */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) - if ((crasher_proc = create_proc_entry(CRASH, 0, NULL)) == NULL) { - return -ENOMEM; - } - crasher_proc->owner = THIS_MODULE - crasher_proc->read_proc = crasher_read; - crasher_proc->write_proc = crasher_write; -#else - crasher_proc = proc_create_data(CRASH, 0, NULL, - &crasher_proc_fops, NULL); - if (!crasher_proc) - return -ENOMEM; -#endif - return 0; -} - -void crasher_exit(void) -{ - remove_proc_entry(CRASH, NULL); - printk("removed crasher module\n"); -} diff --git a/testcases/kdump/lib/kprobes/Makefile b/testcases/kdump/lib/kprobes/Makefile deleted file mode 100755 index 6720b588..00000000 --- a/testcases/kdump/lib/kprobes/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -#obj-m := lkdtm.o crasher.o -obj-m := kprobes.o -KDIR := /lib/modules/$(shell uname -r)/build -PWD := $(shell pwd) -default: - $(MAKE) -C $(KDIR) M=$(PWD) modules - - -clean: - make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean - rm -f Module.symvers -#clean: -# rm -f *.mod.c *.ko *.o .*.cmd *.symvers -# rm -rf .tmp_versions diff --git a/testcases/kdump/lib/kprobes/kprobes.c b/testcases/kdump/lib/kprobes/kprobes.c deleted file mode 100755 index 6f5c50eb..00000000 --- a/testcases/kdump/lib/kprobes/kprobes.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include - -/* - * Jumper probe for do_fork. - * Mirror principle enables access to arguments of the probed routine - * from the probe handler. - */ - -/* Proxy routine having the same arguments as actual do_fork() routine */ -long jdo_fork(unsigned long clone_flags, unsigned long stack_start, - struct pt_regs *regs, unsigned long stack_size, - int __user * parent_tidptr, int __user * child_tidptr) -{ - printk("jprobe: clone_flags=0x%lx, stack_size=0x%lx, regs=%p\n", - clone_flags, stack_size, regs); - /* Always end with a call to jprobe_return(). */ - jprobe_return(); - - return 0; -} - -static struct jprobe my_jprobe = { - .entry = jdo_fork -}; - -static int __init jprobe_init(void) -{ - int ret; - my_jprobe.kp.symbol_name = "do_fork"; - - if ((ret = register_jprobe(&my_jprobe)) < 0) { - printk("register_jprobe failed, returned %d\n", ret); - /* XXX: Exit code is wrong. */ - return -1; - } - printk("Planted jprobe at %p, handler addr %p\n", - my_jprobe.kp.addr, my_jprobe.entry); - return 0; -} - -static void __exit jprobe_exit(void) -{ - unregister_jprobe(&my_jprobe); - printk("jprobe unregistered\n"); -} - -module_init(jprobe_init) - module_exit(jprobe_exit) - MODULE_LICENSE("GPL"); diff --git a/testcases/kdump/lib/lkdtm/Makefile b/testcases/kdump/lib/lkdtm/Makefile deleted file mode 100755 index ba88a80d..00000000 --- a/testcases/kdump/lib/lkdtm/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -obj-m := lkdtm.o -KDIR := /lib/modules/$(shell uname -r)/build -PWD := $(shell pwd) -USE_SYMBOL_NAME := $(shell echo "${USE_SYMBOL_NAME}") - -ifeq (1, $(USE_SYMBOL_NAME)) - EXTRA_CFLAGS := -DUSE_SYMBOL_NAME -endif - -default: - $(MAKE) -C $(KDIR) M=$(PWD) modules - - -clean: - make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean - rm -f Module.symvers -#clean: -# rm -f *.mod.c *.ko *.o .*.cmd *.symvers -# rm -rf .tmp_versions diff --git a/testcases/kdump/lib/lkdtm/lkdtm.c b/testcases/kdump/lib/lkdtm/lkdtm.c deleted file mode 100755 index 7ea8de66..00000000 --- a/testcases/kdump/lib/lkdtm/lkdtm.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Kprobe module for testing crash dumps - * - * 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. - * - * Copyright (C) IBM Corporation, 2006 - * - * Author: Ankita Garg - * Sachin Sant - * Cai Qian - * - * This module induces system failures at predefined crashpoints to - * evaluate the reliability of crash dumps obtained using different dumping - * solutions. - * - * It is adapted from the Linux Kernel Dump Test Tool by - * Fernando Luis Vazquez Cao - * - * Usage : insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<> - * [cpoint_count={>0}] - * - * recur_count : Recursion level for the stack overflow test. Default is 10. - * - * cpoint_name : Crash point where the kernel is to be crashed. It can be - * one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY, - * FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD, - * IDE_CORE_CP - * - * cpoint_type : Indicates the action to be taken on hitting the crash point. - * It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW - * - * cpoint_count : Indicates the number of times the crash point is to be hit - * to trigger an action. The default is 10. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_IDE -#include -#endif - -#define NUM_CPOINTS 8 -#define NUM_CPOINT_TYPES 5 -#define DEFAULT_COUNT 10 -#define REC_NUM_DEFAULT 10 - -enum cname { - INVALID, - INT_HARDWARE_ENTRY, - INT_HW_IRQ_EN, - INT_TASKLET_ENTRY, - FS_DEVRW, - MEM_SWAPOUT, - TIMERADD, - SCSI_DISPATCH_CMD, - IDE_CORE_CP -}; - -enum ctype { - NONE, - PANIC, - BUG, - EXCEPTION, - LOOP, - OVERFLOW -}; - -static char *cp_name[] = { - "INT_HARDWARE_ENTRY", - "INT_HW_IRQ_EN", - "INT_TASKLET_ENTRY", - "FS_DEVRW", - "MEM_SWAPOUT", - "TIMERADD", - "SCSI_DISPATCH_CMD", - "IDE_CORE_CP" -}; - -static char *cp_type[] = { - "PANIC", - "BUG", - "EXCEPTION", - "LOOP", - "OVERFLOW" -}; - -static struct jprobe lkdtm; - -static int lkdtm_parse_commandline(void); -static void lkdtm_handler(void); - -static char *cpoint_name = INVALID; -static char *cpoint_type = NONE; -static int cpoint_count = DEFAULT_COUNT; -static int recur_count = REC_NUM_DEFAULT; - -static enum cname cpoint = INVALID; -static enum ctype cptype = NONE; -static int count = DEFAULT_COUNT; - -module_param(recur_count, int, 0644); -MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, " - "default is 10"); -module_param(cpoint_name, charp, 0644); -MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); -module_param(cpoint_type, charp, 0644); -MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on " - "hitting the crash point"); -module_param(cpoint_count, int, 0644); -MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the " - "crash point is to be hit to trigger action"); - -unsigned int jp_do_irq(unsigned int irq) -{ - lkdtm_handler(); - jprobe_return(); - return 0; -} - -irqreturn_t jp_handle_irq_event(unsigned int irq, struct irqaction * action) -{ - lkdtm_handler(); - jprobe_return(); - return 0; -} - -void jp_tasklet_action(struct softirq_action *a) -{ - lkdtm_handler(); - jprobe_return(); -} - -void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) -{ - lkdtm_handler(); - jprobe_return(); -} - -struct scan_control; - -unsigned long jp_shrink_page_list(struct list_head *page_list, - struct scan_control *sc) -{ - lkdtm_handler(); - jprobe_return(); - return 0; -} - -int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim, - const enum hrtimer_mode mode) -{ - lkdtm_handler(); - jprobe_return(); - return 0; -} - -int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd) -{ - lkdtm_handler(); - jprobe_return(); - return 0; -} - -#ifdef CONFIG_IDE -int jp_generic_ide_ioctl(ide_drive_t * drive, struct file *file, - struct block_device *bdev, unsigned int cmd, - unsigned long arg) -{ - lkdtm_handler(); - jprobe_return(); - return 0; -} -#endif - -static int lkdtm_parse_commandline(void) -{ - int i; - - if (cpoint_name == INVALID || cpoint_type == NONE || - cpoint_count < 1 || recur_count < 1) - return -EINVAL; - - for (i = 0; i < NUM_CPOINTS; ++i) { - if (!strcmp(cpoint_name, cp_name[i])) { - cpoint = i + 1; - break; - } - } - - for (i = 0; i < NUM_CPOINT_TYPES; ++i) { - if (!strcmp(cpoint_type, cp_type[i])) { - cptype = i + 1; - break; - } - } - - if (cpoint == INVALID || cptype == NONE) - return -EINVAL; - - count = cpoint_count; - - return 0; -} - -static int recursive_loop(int a) -{ - char buf[1024]; - - memset(buf, 0xFF, 1024); - recur_count--; - if (!recur_count) - return 0; - else - return recursive_loop(a); -} - -void lkdtm_handler(void) -{ - /* Escape endless loop. */ - if (count < 0) - return; - - printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n", - cpoint_name, cpoint_type); - --count; - - if (count == 0) { - switch (cptype) { - case NONE: - break; - case PANIC: - printk(KERN_INFO "lkdtm : PANIC\n"); - panic("dumptest"); - break; - case BUG: - printk(KERN_INFO "lkdtm : BUG\n"); - BUG(); - break; - case EXCEPTION: - printk(KERN_INFO "lkdtm : EXCEPTION\n"); - *((int *)0) = 0; - break; - case LOOP: - printk(KERN_INFO "lkdtm : LOOP\n"); - for (;;) ; - break; - case OVERFLOW: - printk(KERN_INFO "lkdtm : OVERFLOW\n"); - (void)recursive_loop(0); - break; - default: - break; - } - count = cpoint_count; - } -} - -#ifdef USE_SYMBOL_NAME -void lkdtm_symbol_name(char *name, void (*entry) (void)) -{ - lkdtm.kp.symbol_name = name; - lkdtm.entry = (kprobe_opcode_t *) entry; -} - -#else -void lkdtm_lookup_name(char *name, void (*entry) (void)) -{ - unsigned long addr; - - addr = kallsyms_lookup_name(name); - if (addr) { - *(lkdtm.kp.addr) = addr; - lkdtm.entry = JPROBE_ENTRY(entry); - } else - printk(KERN_INFO "lkdtm : Crash point not available\n"); -} -#endif - -int lkdtm_module_init(void) -{ - int ret; - - if (lkdtm_parse_commandline() == -EINVAL) { - printk(KERN_INFO "lkdtm : Invalid command\n"); - return -EINVAL; - } - - switch (cpoint) { - case INT_HARDWARE_ENTRY: - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".__do_IRQ", (void (*)(void))jp_do_irq); -#else - lkdtm_symbol_name("__do_IRQ", (void (*)(void))jp_do_irq); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("__do_IRQ", (void (*)(void))jp_do_irq); - -#endif /* USE_SYMBOL_NAME */ - break; - - case INT_HW_IRQ_EN: - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".handle_IRQ_event", - (void (*)(void))jp_handle_irq_event); -#else - lkdtm_symbol_name("handle_IRQ_event", - (void (*)(void))jp_handle_irq_event); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("handle_IRQ_event", - (void (*)(void))jp_handle_irq_event); - -#endif /* USE_SYMBOL_NAME */ - break; - - case INT_TASKLET_ENTRY: - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".tasklet_action", - (void (*)(void))jp_tasklet_action); -#else - lkdtm_symbol_name("tasklet_action", - (void (*)(void))jp_tasklet_action); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("tasklet_action", - (void (*)(void))jp_tasklet_action); - -#endif /* USE_SYMBOL_NAME */ - break; - - case FS_DEVRW: - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".ll_rw_block", - (void (*)(void))jp_ll_rw_block); -#else - lkdtm_symbol_name("ll_rw_block", - (void (*)(void))jp_ll_rw_block); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("ll_rw_block", - (void (*)(void))jp_ll_rw_block); - -#endif /* USE_SYMBOL_NAME */ - break; - - case MEM_SWAPOUT: - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".shrink_inactive_list", - (void (*)(void))jp_shrink_page_list); -#else - lkdtm_symbol_name("shrink_inactive_list", - (void (*)(void))jp_shrink_page_list); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("shrink_inactive_list", - (void (*)(void))jp_shrink_page_list); - -#endif /* USE_SYMBOL_NAME */ - break; - - case TIMERADD: - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".hrtimer_start", - (void (*)(void))jp_hrtimer_start); -#else - lkdtm_symbol_name("hrtimer_start", - (void (*)(void))jp_hrtimer_start); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("hrtimer_start", - (void (*)(void))jp_hrtimer_start); - -#endif /* USE_SYMBOL_NAME */ - break; - - case SCSI_DISPATCH_CMD: - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".scsi_dispatch_cmd", - (void (*)(void))jp_scsi_dispatch_cmd); -#else - lkdtm_symbol_name("scsi_dispatch_cmd", - (void (*)(void))jp_scsi_dispatch_cmd); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("scsi_dispatch_cmd", - (void (*)(void))jp_scsi_dispatch_cmd); - -#endif /* USE_SYMBOL_NAME */ - break; - - case IDE_CORE_CP: -#ifdef CONFIG_IDE - -#ifdef USE_SYMBOL_NAME - -#ifdef __powerpc__ - lkdtm_symbol_name(".scsi_dispatch_cmd", - (void (*)(void))jp_scsi_dispatch_cmd); -#else - lkdtm_symbol_name("scsi_dispatch_cmd", - (void (*)(void))jp_scsi_dispatch_cmd); -#endif /*__powerpc__*/ - -#else /* USE_SYMBOL_NAME */ - lkdtm_lookup_name("scsi_dispatch_cmd", - (void (*)(void))jp_scsi_dispatch_cmd); - -#endif /* USE_SYMBOL_NAME */ -#endif /* CONFIG_IDE */ - break; - - default: - printk(KERN_INFO "lkdtm : Invalid Crash Point\n"); - break; - } - - if ((ret = register_jprobe(&lkdtm)) < 0) { - printk(KERN_INFO "lkdtm : Couldn't register jprobe\n"); - return ret; - } - - printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n", - cpoint_name, cpoint_type); - return 0; -} - -void lkdtm_module_exit(void) -{ - unregister_jprobe(&lkdtm); - printk(KERN_INFO "lkdtm : Crash point unregistered\n"); -} - -module_init(lkdtm_module_init); -module_exit(lkdtm_module_exit); - -MODULE_LICENSE("GPL"); diff --git a/testcases/kdump/lib/setup.sh b/testcases/kdump/lib/setup.sh deleted file mode 100755 index 3e53f1ae..00000000 --- a/testcases/kdump/lib/setup.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/sh -ex - -conf=${1} -arch=$(uname -m) -kver=$(uname -r) - -. "${conf}" - -echo "Verify Kernel version >= 2.6.16." -# Kernel might in the following format. -# x.y.z-1.el -# x.y.z.1.el -kx=${kver%%.*} -tmp=${kver#*.} -ky=${tmp%%.*} -tmp=${tmp#*.} -tmp=${tmp%%.*} -kz=${tmp%%-*} - -if [ "${kx}" -lt 2 ]; then - error=1 - -elif [ "${kx}" -eq 2 ]; then - if [ "${ky}" -lt 6 ]; then - error=1 - - elif [ "${ky}" -eq 6 ]; then - if [ "${kz}" -lt 16 ]; then - error=1 - fi - fi -fi - -if [ "${error}" ]; then - echo "Fail: kernel version ${kver} is less than 2.6.16." -fi - - -echo "Verify user is root." -if [ $(id -u) != 0 ]; then - echo "Fail: root is required." - error=1 -fi - - -echo "Verify prerequisite." -if [ ! -x "/sbin/kexec" ]; then - echo "Fail: kexec-tools not found." - error=1 -fi - -if [ ! -d "/lib/modules/${kver}/build" ]; then - echo "Fail: kernel-devel not found." - error=1 -fi - -if [ "${CRASH}" ] && [ "${CRASH}" -eq 1 ]; then - if [ ! -x "/usr/bin/crash" ]; then - echo "Fail: crash not found." - error=1 - fi - - if [ ! -f "${VMLINUX}" ]; then - echo "Fail: kernel-debuginfo not found." - error=1 - fi -fi - -# Final result. -if [ "${error}" ]; then - echo "Please fixed the above failures before continuing." - exit 1 -fi - -echo "Compile Kernel modules." -make clean - -# Test if struct kprobe has "symbol_name" field. -if make -C kprobes >/dev/null 2>&1; then - export USE_SYMBOL_NAME=1 -fi - -make - -echo "Modify Boot Loader." -if [ "${arch}" = "ppc64" ]; then - args="crashkernel=256M@32M xmon=off" -elif [ "${arch}" = "i686" ]; then - args="crashkernel=256M@128M nmi_watchdog=1" -elif [ "${arch}" = "ia64" ]; then - args="crashkernel=512M@256M" -else - args="crashkernel=256M@128M" -fi - -if [ -x "/sbin/grubby" ]; then - /sbin/grubby --default-kernel | - xargs /sbin/grubby --args="${args}" --update-kernel - -else - echo "Warn: please make sure the following arguments are in Boot\ - Loader:" - echo "$args" - echo "Hit any key when ready." - read -fi - -exit 0 diff --git a/testcases/kdump/lib/ssh.tcl b/testcases/kdump/lib/ssh.tcl deleted file mode 100755 index a2267033..00000000 --- a/testcases/kdump/lib/ssh.tcl +++ /dev/null @@ -1,23 +0,0 @@ -set cmd [lindex $argv 0] -set pwd [lindex $argv 1] - -#exp_internal 1 - -# 1 min timeout -#set timeout 60 -set timeout -1 - -eval spawn $cmd - -expect { - -nocase "(yes/no)?" { send "yes\r"; exp_continue } - -nocase "Password:" { send "$pwd\r" } - # Handling ssh keys have already been set up. - eof { exit } - #timeout { exit 1 } -} - -# Wait for eof. -expect - -exit 0 diff --git a/testcases/kdump/lib/sysinfo.sh b/testcases/kdump/lib/sysinfo.sh deleted file mode 100755 index d63b15c5..00000000 --- a/testcases/kdump/lib/sysinfo.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh - -echo "" -echo "------------- HOSTNAME -------------------" -echo "" -hostname -echo "" -echo "------------- /PROC/VERSION --------------" -echo "" -cat /proc/version -echo "" -echo "------------- /PROC/CPUINFO --------------" -echo "" -cat /proc/cpuinfo -echo "" -echo "------------- /PROC/MEMINFO --------------" -echo "" -cat /proc/meminfo -echo "" -echo "------------- /PROC/DEVICES --------------" -echo "" -cat /proc/devices -echo "" -echo "------------- /PROC/INTERRUPTS -----------" -echo "" -cat /proc/interrupts -echo "" -echo "------------- /PROC/IOMEM ----------------" -echo "" -cat /proc/iomem -echo "" -echo "------------- /PROC/PARTITIONS -----------" -echo "" -cat /proc/partitions -echo "" -echo "------------- DF -H ----------------------" -echo "" -df -h -echo "" -echo "------------- LSPCI ----------------------" -echo "" -lspci -echo "" -echo "------------- LSMOD ----------------------" -echo "" -lsmod -echo "" -echo "------------- IFCONFIG -------------------" -echo "" -ifconfig -echo "" -echo "------------- PS -E ----------------------" -echo "" -ps -e diff --git a/testcases/kdump/lib/test.sh b/testcases/kdump/lib/test.sh deleted file mode 100755 index eaec0b5a..00000000 --- a/testcases/kdump/lib/test.sh +++ /dev/null @@ -1,271 +0,0 @@ -#!/bin/sh -xe - -conf=${1}; shift -test=${1} -crasher=crasher -lkdtm=lkdtm - -. "${conf}" - -case "${test}" in - - "KEXEC-L") - kexec -l /boot/vmlinuz --initrd=/boot/initrd \ - --append="$(cat /proc/cmdline)" - sleep 10 - kexec -e - ;; - "MNS") - echo "Not implemented" - ;; - - "MNN") - echo "Not implemented" - ;; - - "MCS") - echo "Not implemented" - ;; - - "MCN") - echo "Not implemented" - ;; - - "MCF") - echo "Not implemented" - ;; - - "ACS") - echo c >/proc/sysrq-trigger - ;; - - "ACP") - # Panic test 0 in crasher module: panic() - insmod "${crasher}"/crasher.ko - echo 0 >/proc/crasher - ;; - - "ACB") - # Panic test 1 in crasher module: BUG() - insmod "${crasher}"/crasher.ko - echo 1 >/proc/crasher - ;; - - "ACE") - # Panic test 2 in crasher module: panic_on_oops - insmod "${crasher}"/crasher.ko - echo 1 >/proc/sys/kernel/panic_on_oops - echo 2 >/proc/crasher - ;; - - "ACL") - # Panic test 3 in crasher module: hang w/double spinlock - # requires nmi_watchdog be enabled - insmod "${crasher}"/crasher.ko - echo 3 >/proc/crasher - ;; - - "KPIDB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HARDWARE_ENTRY cpoint_type=BUG cpoint_count=05 - ;; - "KPIDE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HARDWARE_ENTRY cpoint_type=EXCEPTION cpoint_count=05 - ;; - "KPIDL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HARDWARE_ENTRY cpoint_type=LOOP cpoint_count=05 - ;; - "KPIDP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HARDWARE_ENTRY cpoint_type=PANIC cpoint_count=05 - ;; - "KPIDO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HARDWARE_ENTRY cpoint_type=OVERFLOW cpoint_count=10 - ;; - "KPIEB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HW_IRQ_EN cpoint_type=BUG cpoint_count=10 - ;; - "KPIEE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HW_IRQ_EN cpoint_type=EXCEPTION cpoint_count=10 - ;; - "KPIEL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HW_IRQ_EN cpoint_type=LOOP cpoint_count=10 - ;; - "KPIEP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HW_IRQ_EN cpoint_type=PANIC cpoint_count=10 - ;; - "KPIEO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_HW_IRQ_EN cpoint_type=OVERFLOW cpoint_count=10 - ;; - "KPTEB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_TASKLET_ENTRY cpoint_type=BUG cpoint_count=10 - ;; - "KPTEE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_TASKLET_ENTRY cpoint_type=EXCEPTION cpoint_count=10 - ;; - "KPTEL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_TASKLET_ENTRY cpoint_type=LOOP cpoint_count=10 - ;; - "KPTEP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_TASKLET_ENTRY cpoint_type=PANIC cpoint_count=10 - ;; - "KPTEO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=INT_TASKLET_ENTRY cpoint_type=OVERFLOW cpoint_count=10 - ;; - "KPBB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=FS_DEVRW cpoint_type=BUG cpoint_count=10 - ;; - "KPBE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=FS_DEVRW cpoint_type=EXCEPTION cpoint_count=10 - ;; - "KPBL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=FS_DEVRW cpoint_type=LOOP cpoint_count=10 - ;; - "KPBP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=FS_DEVRW cpoint_type=PANIC cpoint_count=10 - ;; - "KPBO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=FS_DEVRW cpoint_type=OVERFLOW cpoint_count=10 - ;; - "KPMSB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=MEM_SWAPOUT cpoint_type=BUG cpoint_count=10 - ;; - "KPMSE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=MEM_SWAPOUT cpoint_type=EXCEPTION cpoint_count=10 - ;; - "KPMSL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=MEM_SWAPOUT cpoint_type=LOOP cpoint_count=10 - ;; - "KPMSP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=MEM_SWAPOUT cpoint_type=PANIC cpoint_count=10 - ;; - "KPMSO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=MEM_SWAPOUT cpoint_type=OVERFLOW cpoint_count=10 - ;; - "KPTB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=TIMERADD cpoint_type=BUG cpoint_count=10 - ;; - "KPTE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=TIMERADD cpoint_type=EXCEPTION cpoint_count=10 - ;; - "KPTL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=TIMERADD cpoint_type=LOOP cpoint_count=10 - ;; - "KPTP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=TIMERADD cpoint_type=PANIC cpoint_count=10 - ;; - "KPTO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=TIMERADD cpoint_type=OVERFLOW cpoint_count=10 - ;; - "KPSB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=SCSI_DISPATCH_CMD cpoint_type=BUG cpoint_count=10 - ;; - "KPSE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=SCSI_DISPATCH_CMD cpoint_type=EXCEPTION cpoint_count=10 - ;; - "KPSL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=SCSI_DISPATCH_CMD cpoint_type=LOOP cpoint_count=10 - ;; - "KPSP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=SCSI_DISPATCH_CMD cpoint_type=PANIC cpoint_count=10 - ;; - "KPSO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=SCSI_DISPATCH_CMD cpoint_type=OVERFLOW cpoint_count=10 - ;; - "KPIB") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=IDE_CORE_CP cpoint_type=BUG cpoint_count=10 - ;; - "KPIE") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=IDE_CORE_CP cpoint_type=EXCEPTION cpoint_count=10 - ;; - "KPIL") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=IDE_CORE_CP cpoint_type=LOOP cpoint_count=10 - ;; - "KPIP") - insmod "${lkdtm}"/lkdtm.ko cpoint_name=IDE_CORE_CP cpoint_type=PANIC cpoint_count=10 - ;; - "KPIO") - echo 1 >/proc/sys/kernel/panic_on_oops - insmod "${lkdtm}"/lkdtm.ko cpoint_name=IDE_CORE_CP cpoint_type=OVERFLOW cpoint_count=01 - ;; - "KLEXT") - echo "ext3 ${EXT3_PART}" >/etc/kdump.conf - if [ "${FILTER}" -eq 1 ]; then - echo "core_collector makedumpfile ${MAKE_OPTIONS}" >>/etc/kdump.conf - fi - /etc/init.d/kdump restart - echo c >/proc/sysrq-trigger - ;; - - "KLLBL") - echo "ext3 LABEL=${EXT3_LABEL}" >/etc/kdump.conf - if [ "${FILTER}" -eq 1 ]; then - echo "core_collector makedumpfile ${MAKE_OPTIONS}" >>/etc/kdump.conf - fi - /etc/init.d/kdump restart - echo c >/proc/sysrq-trigger - ;; - - "KLUID") - echo "ext3 UUID=${EXT3_UID}" >/etc/kdump.conf - if [ "${FILTER}" -eq 1 ]; then - echo "core_collector makedumpfile ${MAKE_OPTIONS}" >>/etc/kdump.conf - fi - /etc/init.d/kdump restart - echo c >/proc/sysrq-trigger - ;; - - "KLRAW") - echo "raw ${RAW_PART}" >/etc/kdump.conf - if [ "${FILTER}" -eq 1 ]; then - echo "core_collector makedumpfile ${MAKE_OPTIONS}" >>/etc/kdump.conf - fi - /etc/init.d/kdump restart - echo c >/proc/sysrq-trigger - ;; - - "KNSCP") - echo "net ${SCP_PATH}" >/etc/kdump.conf - if [ "${LINK_DELAY}" ]; then - echo "link_delay ${LINK_DELAY}" >>/etc/kdump.conf - fi - - expect -f ./ssh.tcl "/etc/init.d/kdump propagate" "${SCP_PASS}" - if [ "${FILTER}" -eq 1 ]; then - echo "core_collector makedumpfile ${MAKE_OPTIONS}" >>/etc/kdump.conf - fi - /etc/init.d/kdump restart - echo c >/proc/sysrq-trigger - ;; - - "KNNFS") - echo "net ${NFS_PATH}" >/etc/kdump.conf - if [ "${LINK_DELAY}" ]; then - echo "link_delay ${LINK_DELAY}" >>/etc/kdump.conf - fi - - if [ "${FILTER}" -eq 1 ]; then - echo "core_collector makedumpfile ${MAKE_OPTIONS}" >>/etc/kdump.conf - fi - /etc/init.d/kdump restart - echo c >/proc/sysrq-trigger - ;; - - "KDENB") - echo "net ${SCP_PATH}" >/etc/kdump.conf - expect -f ./ssh.tcl "/etc/init.d/kdump propagate" "${SCP_PASS}" - /etc/init.d/kdump restart - ;; - - *) - echo "Unknown test." - ;; - -esac - -exit 0 diff --git a/testcases/kdump/lib/verify.sh b/testcases/kdump/lib/verify.sh deleted file mode 100755 index 9635d378..00000000 --- a/testcases/kdump/lib/verify.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -x - -conf=${1}; shift -vmcore=${1}; shift -crash=${1}; shift - -. "${conf}" - -echo "" -echo "----------------------------------------------------------" -echo " VMCORE SIZE " -echo "----------------------------------------------------------" -echo "" - -ls -lh "${vmcore}" - -echo "" -echo "----------------------------------------------------------" -echo " READELF " -echo "----------------------------------------------------------" -echo "" - -readelf -a "${vmcore}" - -if [ "${crash}" -eq 1 ]; then - echo "" - echo "----------------------------------------------------------" - echo " CRASH " - echo "----------------------------------------------------------" - echo "" - - cat <crash_cmd -mod -mod -S -runq -foreach bt -foreach files -mount -mount -f -mount -i -vm -ascii -net -set -set -v -bt -bt -a -bt -f -bt -e -bt -E -ps -ps -k -ps -u -ps -s -dev -dev -p -kmem -i -kmem -s -task -exit -EOF - - crash -i crash_cmd "${VMLINUX}" "${vmcore}" - rm -f crash_cmd -fi diff --git a/testcases/kdump/runkdump.conf b/testcases/kdump/runkdump.conf deleted file mode 100755 index d094df13..00000000 --- a/testcases/kdump/runkdump.conf +++ /dev/null @@ -1,76 +0,0 @@ -# Repeation of test. -ITERATION=1 - -# Vmcore verification using crash command. -# Require kernel-debuginfo and crash packages. -CRASH=0 - -# Reboot count. New test should start from 0. -REBOOT=0 - -# Kernel debuginfo. -VMLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" - -# Where vmcore will be saved. -# /var/crash for Red Hat. -# /var/log/dump for SUSE. -COREDIR=/var/crash - -# Enable dump with filtering. -FILTER=0 -# makedumpfile options. -MAKE_OPTIONS= - -# Delay in seconds when dumping to a network destination. -LINK_DELAY= - -# The list of testcases. Please see doc for the overview of -# those tests. - -# ACS ACP ACB ACE ACL -CRASHER="ACS ACP ACB ACE ACL" - -# KPIDB KPIEB KPTEE KPBL -BASIC_LKDTM="KPIDB KPIEB KPTEE KPBL" - -# KPIDE KPIDL KPIDP KPIDO -# KPIEE KPIEL KPIEP KPIEO -# KPTEB KPTEL KPTEP KPTEO -# KPBB KPBE KPBP KPBO -# KPMSB KPMSE KPMSL KPMSP KPMSO -# KPTB KPTE KPTL KPTP KPTO -# KPSB KPSE KPSL KPSP KPSO -# KPIB KPIE KPIL KPIP KPIO -EXTRA_LKDTM=" -KPIDE KPIDL KPIDP KPIDO -KPIEE KPIEL KPIEP KPIEO -KPTEB KPTEL KPTEP KPTEO -KPBB KPBE KPBP KPBO -KPMSB KPMSE KPMSL KPMSP KPMSO -KPTB KPTE KPTL KPTP KPTO -KPSB KPSE KPSL KPSP KPSO -KPIB KPIE KPIL KPIP KPIO -" - -# KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB -EXTRA_DUMP="KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB" - -# Dump vmcore on an EXT3 partition. -EXT3_PART= - -# Dump vmcore on an EXT3 partition with a LABEL. -EXT3_LABEL= - -# Dump vmcore on an EXT3 partition with a partition UID. -EXT3_UID= - -# Dump vmcore on a RAW partition. -RAW_PART= - -# Dump vmcore on network. -SCP_PATH= -# Password for user@server. -SCP_PASS= - -# Dump vmcore on a mounted NFS filesystem. -NFS_PATH= diff --git a/testcases/kdump/runkdump.sh b/testcases/kdump/runkdump.sh deleted file mode 100755 index cd6ee1ca..00000000 --- a/testcases/kdump/runkdump.sh +++ /dev/null @@ -1,282 +0,0 @@ -#!/bin/sh -xe - -SetupCrontab () -{ - echo "Setup crontab." - - set +e - crontab -r - set -e - - # crontab in some distros will not read from STDIN. - - cat <kdump.cron -SHELL=/bin/sh -PATH=/usr/bin:/usr/sbin:/sbin:/bin -MAILTO=root -@reboot cd "$(pwd)"; cd ..; ${0} >>/tmp/kdump-$(date +%F-%T).log 2>&1 -EOF - - crontab kdump.cron - - echo "Enable cron daemon by default." - - if [ -f /etc/init.d/crond ]; then - cron=crond - else - # SUSE - cron=cron - fi - - # Red Hat and SUSE. - if [ -x "/sbin/chkconfig" ]; then - /sbin/chkconfig "${cron}" on - - # Debian and Ubuntu. - elif [ -x "/sbin/update-rc.d" ]; then - /sbin/update-rc.d "${cron}" defaults - fi -} - -SetupKdump () -{ - echo "Start kdump daemon." - /etc/init.d/kdump restart - - echo "Enable kdump daemon by default." - # Red Hat and SUSE. - if [ -x "/sbin/chkconfig" ]; then - /sbin/chkconfig kdump on - - # Debian and Ubuntu. - elif [ -x "/sbin/update-rc.d" ]; then - /sbin/update-rc.d kdump defaults - fi -} - - -PrepareVerify () -{ - if [ "${last}" = "KLEXT" ]; then - # If not mountable, skip it, and continue doing the test. - set +e - mount "${EXT3_PART}" /mnt - set -e - - COREDIR=/mnt"${COREDIR}" - - elif [ "${last}" = "KLLBL" ]; then - # If not mountable, skip it, and continue doing the test. - set +e - mount -L "${EXT3_LABEL}" /mnt - set -e - - COREDIR=/mnt"${COREDIR}" - - elif [ "${last}" = "KLUID" ]; then - # If not mountable, skip it, and continue doing the test. - set +e - mount "/dev/disk/by-uuid/${EXT3_UID}" /mnt - set -e - - COREDIR=/mnt"${COREDIR}" - - elif [ "${last}" = "KLRAW" ]; then - mkdir -p "${COREDIR}/${last}" - - # If not dumpable, skip it, and continue doing the test. - set +e - dd if="${RAW_PART}" of="${COREDIR}/${last}/vmcore" bs=1024 - set -e - - elif [ "${last}" = "KNSCP" ]; then - if [ -z "${SCP_PATH}" ]; then - echo "Fail: network destination not defined." - exit 1 - fi - - file=$(ssh -i ~/.ssh/kdump_id_rsa "${SCP_PATH}" "ls -t ${COREDIR}/*/vmcore* \ - 2>/dev/null | head -1") - - mkdir -p "${COREDIR}/${last}" - - if [ "${file}" ]; then - # Not fatal error. - set +e - scp -i ~/.ssh/kdump_id_rsa "${SCP_PATH}:${file}" "${COREDIR}/${last}" - set -e - fi - - elif [ "${last}" = "KNNFS" ]; then - # Not fatal error. - set +e - mount "${NFS_PATH}" /mnt - set -e - - COREDIR=/mnt"${COREDIR}" - fi - - vmcore=$(ls -t "${COREDIR}"/*/vmcore* 2>/dev/null | head -1) -} - -VerifyTest () -{ - # Should not be here. - if [ -z "${last}" ]; then - echo "Should not be here!" - echo "There must be something wrong with the test setup." - exit 1 - fi - - echo "Verifying the result of previous test ${last}." - ldir=$(ls -td "../${log}/$(hostname)."* | head -1) - - if [ -f "${vmcore}" ]; then - echo "$(date +%F-%T): verification of test ${last} passed." \ - >>"${ldir}/status" - - ./verify.sh "../${conf}" "${vmcore}" "${CRASH}" \ - >>"${ldir}/${ITERATION}.${last}.$(date +%F-%T)" - - # Be careful to define COREDIR. - rm -rf "${COREDIR}"/* - - else - echo "$(date +%F-%T): verification of test ${last} failed:\ - vmcore NOT FOUND." >>"${ldir}/status" - echo "vmcore NOT FOUND." \ - >>"${ldir}/${ITERATION}.${last}.$(date +%F-%T)" - fi -} - -RunTest () -{ - - sed -i "s/\(^REBOOT\)=.*/\1=$((count + 1))/" \ - "../${conf}" - - echo "Running current test ${i}." - - echo "$(date +%F-%T): running current test ${i}." \ - >> "${ldir}/status" - - # Save STDIO buffers. - sync - ./test.sh "../${conf}" "${i}" "../${log}" -} - -# Start test. -conf="./runkdump.conf" -lib="lib" -log="log" - -# Read test configuration file. -. "${conf}" - -# Check mandatory variables. -if [ -z "${ITERATION}" ] || [ -z "${REBOOT}" ] || [ -z "${COREDIR}" ] -then - echo "Fail: some mandatory variables are missing from\ - configuration file." - exit 1 -fi - -cd "${lib}" - -while [ "${ITERATION}" -ge 1 ]; do - -# Reboot the machine first to take advantage of boot parameter -# changes. -if [ -z "${REBOOT}" ] || [ "${REBOOT}" -eq 0 ]; then - echo "Setup test environment." - - SetupCrontab - - ./setup.sh "../${conf}" - - sed -i 's/\(^REBOOT\)=.*/\1=1/' "../${conf}" - - echo "System is going to reboot." - /sbin/shutdown -r now - sleep 60 - -else - count=1 - - for i in ${CRASHER} ${BASIC_LKDTM} ${EXTRA_LKDTM} ${EXTRA_DUMP} \ - END; do - - if [ "${count}" -eq "${REBOOT}" ]; then - # Wait for machine fully booted. - sleep 60 - - # First Test. - if [ "${REBOOT}" -eq 1 ]; then - echo "First test..." - echo "Verify Boot Loader." - if ! grep 'crashkernel=' /proc/cmdline; then - echo "Fail: error changing Boot Loader, no crashkernel=." - exit 1 - fi - - SetupKdump - - # Creat log directory. - mkdir -p "../${log}/$(hostname).$(date +%F-%T)" - - echo "Gather system information." - - ldir=$(ls -td "../${log}/$(hostname)."* | head -1) - ./sysinfo.sh >"${ldir}/system.info" - - else - PrepareVerify - - VerifyTest - - if [ "${i}" = END ]; then - # We are done. - break - fi - - fi - - RunTest - - # Some tests could not reboot target. They can hung up - # machine or leave it working. But we need to do all - # tests. So we are going to reboot if we are in wrong - # place. - - sleep 3600 - echo "$(date +%F-%T): manually reboot for test ${i}." >>"${ldir}/status" - /sbin/shutdown -r now - sleep 60 - fi - - # No test is scheduled to run. - count=$((count + 1)) - last=${i} - done -fi - -if [ "${ITERATION}" -eq 1 ]; then - # We are done. - break - -else - # Run the next iteration. - sed -i "s/\(^ITERATION\)=.*/\1=$((ITERATION - 1))/" \ - "../${conf}" -fi - -done - -# We are done. -# Reset. -sed -i "s/\(^REBOOT\)=.*/\1=0/" "../${conf}" -crontab -r -ldir=$(ls -td "../${log}/$(hostname)."* | head -1) -echo "$(date +%F-%T): test run complete." >>"${ldir}/status" - -exit 0 diff --git a/testcases/kdump/sample/runkdump.BASIC_LKDTM.RHEL b/testcases/kdump/sample/runkdump.BASIC_LKDTM.RHEL deleted file mode 100755 index 15a6e542..00000000 --- a/testcases/kdump/sample/runkdump.BASIC_LKDTM.RHEL +++ /dev/null @@ -1,76 +0,0 @@ -# Repeation of test. -ITERATION=1 - -# Vmcore verification using crash command. -# Require kernel-debuginfo and crash packages. -CRASH=0 - -# Reboot count. New test should start from 0. -REBOOT=0 - -# Kernel debuginfo. -VMLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" - -# Where vmcore will be saved. -# /var/crash for Red Hat. -# /var/log/dump for SUSE. -COREDIR=/var/crash - -# Enable dump with filtering. -FILTER=0 -# makedumpfile options. -MAKE_OPTIONS= - -# Delay in seconds when dumping to a network destination. -LINK_DELAY= - -# The list of testcases. Please see doc for the overview of -# those tests. - -# ACS ACP ACB ACE ACL -#CRASHER="ACS ACP ACB ACE ACL" - -# KPIDB KPIEB KPTEE KPBL -BASIC_LKDTM="KPIDB KPIEB KPTEE KPBL" - -# KPIDE KPIDL KPIDP KPIDO -# KPIEE KPIEL KPIEP KPIEO -# KPTEB KPTEL KPTEP KPTEO -# KPBB KPBE KPBP KPBO -# KPMSB KPMSE KPMSL KPMSP KPMSO -# KPTB KPTE KPTL KPTP KPTO -# KPSB KPSE KPSL KPSP KPSO -# KPIB KPIE KPIL KPIP KPIO -EXTRA_LKDTM=" -KPIDE KPIDL KPIDP KPIDO -KPIEE KPIEL KPIEP KPIEO -KPTEB KPTEL KPTEP KPTEO -KPBB KPBE KPBP KPBO -KPMSB KPMSE KPMSL KPMSP KPMSO -KPTB KPTE KPTL KPTP KPTO -KPSB KPSE KPSL KPSP KPSO -KPIB KPIE KPIL KPIP KPIO -" - -# KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB -#EXTRA_DUMP="KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB" - -# Dump vmcore on an EXT3 partition. -EXT3_PART= - -# Dump vmcore on an EXT3 partition with a LABEL. -EXT3_LABEL= - -# Dump vmcore on an EXT3 partition with a partition UID. -EXT3_UID= - -# Dump vmcore on a RAW partition. -RAW_PART= - -# Dump vmcore on network. -SCP_PATH= -# Password for user@server. -SCP_PASS= - -# Dump vmcore on a mounted NFS filesystem. -NFS_PATH= diff --git a/testcases/kdump/sample/runkdump.BASIC_LKDTM.SLES b/testcases/kdump/sample/runkdump.BASIC_LKDTM.SLES deleted file mode 100755 index b1f8fc00..00000000 --- a/testcases/kdump/sample/runkdump.BASIC_LKDTM.SLES +++ /dev/null @@ -1,76 +0,0 @@ -# Repeation of test. -ITERATION=1 - -# Vmcore verification using crash command. -# Require kernel-debuginfo and crash packages. -CRASH=0 - -# Reboot count. New test should start from 0. -REBOOT=0 - -# Kernel debuginfo. -VMLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" - -# Where vmcore will be saved. -# /var/crash for Red Hat. -# /var/log/dump for SUSE. -COREDIR=/var/log/dump - -# Enable dump with filtering. -FILTER=0 -# makedumpfile options. -MAKE_OPTIONS= - -# Delay in seconds when dumping to a network destination. -LINK_DELAY= - -# The list of testcases. Please see doc for the overview of -# those tests. - -# ACS ACP ACB ACE ACL -#CRASHER="ACS ACP ACB ACE ACL" - -# KPIDB KPIEB KPTEE KPBL -BASIC_LKDTM="KPIDB KPIEB KPTEE KPBL" - -# KPIDE KPIDL KPIDP KPIDO -# KPIEE KPIEL KPIEP KPIEO -# KPTEB KPTEL KPTEP KPTEO -# KPBB KPBE KPBP KPBO -# KPMSB KPMSE KPMSL KPMSP KPMSO -# KPTB KPTE KPTL KPTP KPTO -# KPSB KPSE KPSL KPSP KPSO -# KPIB KPIE KPIL KPIP KPIO -EXTRA_LKDTM=" -KPIDE KPIDL KPIDP KPIDO -KPIEE KPIEL KPIEP KPIEO -KPTEB KPTEL KPTEP KPTEO -KPBB KPBE KPBP KPBO -KPMSB KPMSE KPMSL KPMSP KPMSO -KPTB KPTE KPTL KPTP KPTO -KPSB KPSE KPSL KPSP KPSO -KPIB KPIE KPIL KPIP KPIO -" - -# KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB -#EXTRA_DUMP="KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB" - -# Dump vmcore on an EXT3 partition. -EXT3_PART= - -# Dump vmcore on an EXT3 partition with a LABEL. -EXT3_LABEL= - -# Dump vmcore on an EXT3 partition with a partition UID. -EXT3_UID= - -# Dump vmcore on a RAW partition. -RAW_PART= - -# Dump vmcore on network. -SCP_PATH= -# Password for user@server. -SCP_PASS= - -# Dump vmcore on a mounted NFS filesystem. -NFS_PATH= diff --git a/testcases/kdump/sample/runkdump.CRASHER.RHEL b/testcases/kdump/sample/runkdump.CRASHER.RHEL deleted file mode 100755 index d094df13..00000000 --- a/testcases/kdump/sample/runkdump.CRASHER.RHEL +++ /dev/null @@ -1,76 +0,0 @@ -# Repeation of test. -ITERATION=1 - -# Vmcore verification using crash command. -# Require kernel-debuginfo and crash packages. -CRASH=0 - -# Reboot count. New test should start from 0. -REBOOT=0 - -# Kernel debuginfo. -VMLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" - -# Where vmcore will be saved. -# /var/crash for Red Hat. -# /var/log/dump for SUSE. -COREDIR=/var/crash - -# Enable dump with filtering. -FILTER=0 -# makedumpfile options. -MAKE_OPTIONS= - -# Delay in seconds when dumping to a network destination. -LINK_DELAY= - -# The list of testcases. Please see doc for the overview of -# those tests. - -# ACS ACP ACB ACE ACL -CRASHER="ACS ACP ACB ACE ACL" - -# KPIDB KPIEB KPTEE KPBL -BASIC_LKDTM="KPIDB KPIEB KPTEE KPBL" - -# KPIDE KPIDL KPIDP KPIDO -# KPIEE KPIEL KPIEP KPIEO -# KPTEB KPTEL KPTEP KPTEO -# KPBB KPBE KPBP KPBO -# KPMSB KPMSE KPMSL KPMSP KPMSO -# KPTB KPTE KPTL KPTP KPTO -# KPSB KPSE KPSL KPSP KPSO -# KPIB KPIE KPIL KPIP KPIO -EXTRA_LKDTM=" -KPIDE KPIDL KPIDP KPIDO -KPIEE KPIEL KPIEP KPIEO -KPTEB KPTEL KPTEP KPTEO -KPBB KPBE KPBP KPBO -KPMSB KPMSE KPMSL KPMSP KPMSO -KPTB KPTE KPTL KPTP KPTO -KPSB KPSE KPSL KPSP KPSO -KPIB KPIE KPIL KPIP KPIO -" - -# KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB -EXTRA_DUMP="KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB" - -# Dump vmcore on an EXT3 partition. -EXT3_PART= - -# Dump vmcore on an EXT3 partition with a LABEL. -EXT3_LABEL= - -# Dump vmcore on an EXT3 partition with a partition UID. -EXT3_UID= - -# Dump vmcore on a RAW partition. -RAW_PART= - -# Dump vmcore on network. -SCP_PATH= -# Password for user@server. -SCP_PASS= - -# Dump vmcore on a mounted NFS filesystem. -NFS_PATH= diff --git a/testcases/kdump/sample/runkdump.CRASHER.SLES b/testcases/kdump/sample/runkdump.CRASHER.SLES deleted file mode 100755 index d708e0c9..00000000 --- a/testcases/kdump/sample/runkdump.CRASHER.SLES +++ /dev/null @@ -1,76 +0,0 @@ -# Repeation of test. -ITERATION=1 - -# Vmcore verification using crash command. -# Require kernel-debuginfo and crash packages. -CRASH=0 - -# Reboot count. New test should start from 0. -REBOOT=0 - -# Kernel debuginfo. -VMLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" - -# Where vmcore will be saved. -# /var/crash for Red Hat. -# /var/log/dump for SUSE. -COREDIR=/var/log/dump - -# Enable dump with filtering. -FILTER=0 -# makedumpfile options. -MAKE_OPTIONS= - -# Delay in seconds when dumping to a network destination. -LINK_DELAY= - -# The list of testcases. Please see doc for the overview of -# those tests. - -# ACS ACP ACB ACE ACL -CRASHER="ACS ACP ACB ACE ACL" - -# KPIDB KPIEB KPTEE KPBL -BASIC_LKDTM="KPIDB KPIEB KPTEE KPBL" - -# KPIDE KPIDL KPIDP KPIDO -# KPIEE KPIEL KPIEP KPIEO -# KPTEB KPTEL KPTEP KPTEO -# KPBB KPBE KPBP KPBO -# KPMSB KPMSE KPMSL KPMSP KPMSO -# KPTB KPTE KPTL KPTP KPTO -# KPSB KPSE KPSL KPSP KPSO -# KPIB KPIE KPIL KPIP KPIO -EXTRA_LKDTM=" -KPIDE KPIDL KPIDP KPIDO -KPIEE KPIEL KPIEP KPIEO -KPTEB KPTEL KPTEP KPTEO -KPBB KPBE KPBP KPBO -KPMSB KPMSE KPMSL KPMSP KPMSO -KPTB KPTE KPTL KPTP KPTO -KPSB KPSE KPSL KPSP KPSO -KPIB KPIE KPIL KPIP KPIO -" - -# KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB -EXTRA_DUMP="KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB" - -# Dump vmcore on an EXT3 partition. -EXT3_PART= - -# Dump vmcore on an EXT3 partition with a LABEL. -EXT3_LABEL= - -# Dump vmcore on an EXT3 partition with a partition UID. -EXT3_UID= - -# Dump vmcore on a RAW partition. -RAW_PART= - -# Dump vmcore on network. -SCP_PATH= -# Password for user@server. -SCP_PASS= - -# Dump vmcore on a mounted NFS filesystem. -NFS_PATH= diff --git a/testcases/kdump/sample/runkdump.EXTRA_LKDTM.RHEL b/testcases/kdump/sample/runkdump.EXTRA_LKDTM.RHEL deleted file mode 100755 index aac72f82..00000000 --- a/testcases/kdump/sample/runkdump.EXTRA_LKDTM.RHEL +++ /dev/null @@ -1,76 +0,0 @@ -# Repeation of test. -ITERATION=1 - -# Vmcore verification using crash command. -# Require kernel-debuginfo and crash packages. -CRASH=0 - -# Reboot count. New test should start from 0. -REBOOT=0 - -# Kernel debuginfo. -VMLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" - -# Where vmcore will be saved. -# /var/crash for Red Hat. -# /var/log/dump for SUSE. -COREDIR=/var/crash - -# Enable dump with filtering. -FILTER=0 -# makedumpfile options. -MAKE_OPTIONS= - -# Delay in seconds when dumping to a network destination. -LINK_DELAY= - -# The list of testcases. Please see doc for the overview of -# those tests. - -# ACS ACP ACB ACE ACL -#CRASHER="ACS ACP ACB ACE ACL" - -# KPIDB KPIEB KPTEE KPBL -#BASIC_LKDTM="KPIDB KPIEB KPTEE KPBL" - -# KPIDE KPIDL KPIDP KPIDO -# KPIEE KPIEL KPIEP KPIEO -# KPTEB KPTEL KPTEP KPTEO -# KPBB KPBE KPBP KPBO -# KPMSB KPMSE KPMSL KPMSP KPMSO -# KPTB KPTE KPTL KPTP KPTO -# KPSB KPSE KPSL KPSP KPSO -# KPIB KPIE KPIL KPIP KPIO -EXTRA_LKDTM=" -KPIDE KPIDL KPIDP KPIDO -KPIEE KPIEL KPIEP KPIEO -KPTEB KPTEL KPTEP KPTEO -KPBB KPBE KPBP KPBO -KPMSB KPMSE KPMSL KPMSP KPMSO -KPTB KPTE KPTL KPTP KPTO -KPSB KPSE KPSL KPSP KPSO -KPIB KPIE KPIL KPIP KPIO -" - -# KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB -#EXTRA_DUMP="KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB" - -# Dump vmcore on an EXT3 partition. -EXT3_PART= - -# Dump vmcore on an EXT3 partition with a LABEL. -EXT3_LABEL= - -# Dump vmcore on an EXT3 partition with a partition UID. -EXT3_UID= - -# Dump vmcore on a RAW partition. -RAW_PART= - -# Dump vmcore on network. -SCP_PATH= -# Password for user@server. -SCP_PASS= - -# Dump vmcore on a mounted NFS filesystem. -NFS_PATH= diff --git a/testcases/kdump/sample/runkdump.EXTRA_LKDTM.SLES b/testcases/kdump/sample/runkdump.EXTRA_LKDTM.SLES deleted file mode 100755 index 2f78d0cb..00000000 --- a/testcases/kdump/sample/runkdump.EXTRA_LKDTM.SLES +++ /dev/null @@ -1,76 +0,0 @@ -# Repeation of test. -ITERATION=1 - -# Vmcore verification using crash command. -# Require kernel-debuginfo and crash packages. -CRASH=0 - -# Reboot count. New test should start from 0. -REBOOT=0 - -# Kernel debuginfo. -VMLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" - -# Where vmcore will be saved. -# /var/crash for Red Hat. -# /var/log/dump for SUSE. -COREDIR=/var/log/dump - -# Enable dump with filtering. -FILTER=0 -# makedumpfile options. -MAKE_OPTIONS= - -# Delay in seconds when dumping to a network destination. -LINK_DELAY= - -# The list of testcases. Please see doc for the overview of -# those tests. - -# ACS ACP ACB ACE ACL -#CRASHER="ACS ACP ACB ACE ACL" - -# KPIDB KPIEB KPTEE KPBL -#BASIC_LKDTM="KPIDB KPIEB KPTEE KPBL" - -# KPIDE KPIDL KPIDP KPIDO -# KPIEE KPIEL KPIEP KPIEO -# KPTEB KPTEL KPTEP KPTEO -# KPBB KPBE KPBP KPBO -# KPMSB KPMSE KPMSL KPMSP KPMSO -# KPTB KPTE KPTL KPTP KPTO -# KPSB KPSE KPSL KPSP KPSO -# KPIB KPIE KPIL KPIP KPIO -EXTRA_LKDTM=" -KPIDE KPIDL KPIDP KPIDO -KPIEE KPIEL KPIEP KPIEO -KPTEB KPTEL KPTEP KPTEO -KPBB KPBE KPBP KPBO -KPMSB KPMSE KPMSL KPMSP KPMSO -KPTB KPTE KPTL KPTP KPTO -KPSB KPSE KPSL KPSP KPSO -KPIB KPIE KPIL KPIP KPIO -" - -# KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB -#EXTRA_DUMP="KLEXT KLLBL KLUID KLRAW KNSCP KNNFS KDENB" - -# Dump vmcore on an EXT3 partition. -EXT3_PART= - -# Dump vmcore on an EXT3 partition with a LABEL. -EXT3_LABEL= - -# Dump vmcore on an EXT3 partition with a partition UID. -EXT3_UID= - -# Dump vmcore on a RAW partition. -RAW_PART= - -# Dump vmcore on network. -SCP_PATH= -# Password for user@server. -SCP_PASS= - -# Dump vmcore on a mounted NFS filesystem. -NFS_PATH= diff --git a/testcases/kernel/Makefile b/testcases/kernel/Makefile index bf890e17..98fd45a9 100755 --- a/testcases/kernel/Makefile +++ b/testcases/kernel/Makefile @@ -15,7 +15,6 @@ SUBDIRS := syscalls # Build lib SUBDIRS += lib -ifneq ($(UCLINUX),1) # KEEP THIS LIST ALPHABETIZED PLEASE! SUBDIRS += connectors \ containers \ @@ -32,6 +31,7 @@ SUBDIRS += connectors \ logging \ mem \ numa \ + power_management \ pty \ sched \ security \ @@ -40,13 +40,8 @@ SUBDIRS += connectors \ uevents \ watchqueue \ -ifeq ($(WITH_POWER_MANAGEMENT_TESTSUITE),yes) -SUBDIRS += power_management -endif - ifeq ($(WITH_KVM_TESTSUITE),yes) SUBDIRS += kvm -endif endif diff --git a/testcases/kernel/containers/mountns/mountns01.c b/testcases/kernel/containers/mountns/mountns01.c index 8d821ea4..749d554c 100755 --- a/testcases/kernel/containers/mountns/mountns01.c +++ b/testcases/kernel/containers/mountns/mountns01.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Red Hat, Inc. * Copyright (C) 2021 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Tests a shared mount: shared mount can be replicated to as many * mountpoints and all the replicas continue to be exactly same. * @@ -19,14 +17,18 @@ * - Makes directory DIR_A shared * - Clones a new child process with CLONE_NEWNS flag * - There are two test cases (where X is parent namespace and Y child namespace): - * 1. First test case - * .. X: bind mounts DIR_B to DIR_A - * .. Y: must see DIR_A/"B" - * .. X: umounts DIR_A - * 2. Second test case - * .. Y: bind mounts DIR_B to DIR_A - * .. X: must see DIR_A/"B" - * .. Y: umounts DIR_A + * + * 1. First test case + * + * - X: bind mounts DIR_B to DIR_A + * - Y: must see DIR_A/"B" + * - X: umounts DIR_A + * + * 2. Second test case + * + * - Y: bind mounts DIR_B to DIR_A + * - X: must see DIR_A/"B" + * - Y: umounts DIR_A */ #include diff --git a/testcases/kernel/containers/mountns/mountns02.c b/testcases/kernel/containers/mountns/mountns02.c index e7a80cbb..0dda86f3 100755 --- a/testcases/kernel/containers/mountns/mountns02.c +++ b/testcases/kernel/containers/mountns/mountns02.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Red Hat, Inc. * Copyright (C) 2021 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Tests a private mount: private mount does not forward or receive * propagation. * @@ -20,14 +18,18 @@ * - Clones a new child process with CLONE_NEWNS flag * - There are two test cases (where X is parent namespace and Y child * namespace): - * 1. First test case - * .. X: bind mounts DIR_B to DIR_A - * .. Y: must see DIR_A/"A" and must not see DIR_A/"B" - * .. X: umounts DIR_A - * 2. Second test case - * .. Y: bind mounts DIR_B to DIR_A - * .. X: must see DIR_A/"A" and must not see DIR_A/"B" - * .. Y: umounts DIRA + * + * 1. First test case + * + * - X: bind mounts DIR_B to DIR_A + * - Y: must see DIR_A/"A" and must not see DIR_A/"B" + * - X: umounts DIR_A + * + * 2. Second test case + * + * - Y: bind mounts DIR_B to DIR_A + * - X: must see DIR_A/"A" and must not see DIR_A/"B" + * - Y: umounts DIRA */ #include diff --git a/testcases/kernel/containers/mountns/mountns03.c b/testcases/kernel/containers/mountns/mountns03.c index 6066d1c5..c8df1763 100755 --- a/testcases/kernel/containers/mountns/mountns03.c +++ b/testcases/kernel/containers/mountns/mountns03.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Red Hat, Inc. * Copyright (C) 2021 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Tests a slave mount: slave mount is like a shared mount except that * mount and umount events only propagate towards it. * @@ -21,15 +19,19 @@ * mount * - There are two testcases (where X is parent namespace and Y child * namespace): - * 1. First test case - * .. X: bind mounts DIRB to DIRA - * .. Y: must see the file DIRA/"B" - * .. X: umounts DIRA - * 2. Second test case - * .. Y: bind mounts DIRB to DIRA - * .. X: must see only the DIRA/"A" and must not see DIRA/"B" (as slave mount does - * not forward propagation) - * .. Y: umounts DIRA + * + * 1. First test case + * + * - X: bind mounts DIRB to DIRA + * - Y: must see the file DIRA/"B" + * - X: umounts DIRA + * + * 2. Second test case + * + * - Y: bind mounts DIRB to DIRA + * - X: must see only the DIRA/"A" and must not see DIRA/"B" (as slave mount does + * not forward propagation) + * - Y: umounts DIRA */ #include diff --git a/testcases/kernel/containers/mountns/mountns04.c b/testcases/kernel/containers/mountns/mountns04.c index 7b4dcb16..b87fc855 100755 --- a/testcases/kernel/containers/mountns/mountns04.c +++ b/testcases/kernel/containers/mountns/mountns04.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Red Hat, Inc. * Copyright (C) 2021 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Tests an unbindable mount: unbindable mount is an unbindable * private mount. * diff --git a/testcases/kernel/containers/mqns/mqns_01.c b/testcases/kernel/containers/mqns/mqns_01.c index d9f6e6c1..30e06edf 100755 --- a/testcases/kernel/containers/mqns/mqns_01.c +++ b/testcases/kernel/containers/mqns/mqns_01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Create a mqueue inside the parent and check if it can be accessed from * the child namespace. Isolated and unshared process can't access to parent, * but plain process can. diff --git a/testcases/kernel/containers/mqns/mqns_02.c b/testcases/kernel/containers/mqns/mqns_02.c index 4348be7f..dc1b215d 100755 --- a/testcases/kernel/containers/mqns/mqns_02.c +++ b/testcases/kernel/containers/mqns/mqns_02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2009 * Copyright (c) Nadia Derbey, 2009 @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Create a mqueue with the same name in both parent and isolated/forked child, * then check namespace isolation. */ diff --git a/testcases/kernel/containers/mqns/mqns_03.c b/testcases/kernel/containers/mqns/mqns_03.c index 4a0399dd..ac831155 100755 --- a/testcases/kernel/containers/mqns/mqns_03.c +++ b/testcases/kernel/containers/mqns/mqns_03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test mqueuefs from an isolated/forked process namespace. * * [Algorithm] @@ -132,7 +130,7 @@ static void cleanup(void) if (!access(MQUEUE2, F_OK)) SAFE_MQ_UNLINK(MQNAME2); - if (tst_is_mounted(DEVDIR)) + if (!access(DEVDIR, F_OK) && tst_is_mounted(DEVDIR)) SAFE_UMOUNT(DEVDIR); } diff --git a/testcases/kernel/containers/mqns/mqns_04.c b/testcases/kernel/containers/mqns/mqns_04.c index 2d943e1b..790607ec 100755 --- a/testcases/kernel/containers/mqns/mqns_04.c +++ b/testcases/kernel/containers/mqns/mqns_04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test mqueuefs manipulation from child/parent namespaces. * * [Algorithm] @@ -125,7 +123,7 @@ static void cleanup(void) if (!access(MQUEUE2, F_OK)) SAFE_MQ_UNLINK(MQNAME2); - if (tst_is_mounted(DEVDIR)) + if (!access(DEVDIR, F_OK) && tst_is_mounted(DEVDIR)) SAFE_UMOUNT(DEVDIR); } diff --git a/testcases/kernel/containers/netns/netns_netlink.c b/testcases/kernel/containers/netns/netns_netlink.c index e8df616e..5246b066 100755 --- a/testcases/kernel/containers/netns/netns_netlink.c +++ b/testcases/kernel/containers/netns/netns_netlink.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Red Hat, Inc. * Copyright (c) 2021 Petr Vorel */ /*\ - * [Description] - * * Tests a netlink interface inside a new network namespace. * * - Unshares a network namespace (so network related actions diff --git a/testcases/kernel/containers/pidns/pidns01.c b/testcases/kernel/containers/pidns/pidns01.c index 8b856ec9..94b8f16d 100755 --- a/testcases/kernel/containers/pidns/pidns01.c +++ b/testcases/kernel/containers/pidns/pidns01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and check: * * - child process ID must be 1 diff --git a/testcases/kernel/containers/pidns/pidns02.c b/testcases/kernel/containers/pidns/pidns02.c index f23178cb..b5e2d607 100755 --- a/testcases/kernel/containers/pidns/pidns02.c +++ b/testcases/kernel/containers/pidns/pidns02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and check: * * - child session ID must be 1 diff --git a/testcases/kernel/containers/pidns/pidns03.c b/testcases/kernel/containers/pidns/pidns03.c index d662ca9d..31fdb3e4 100755 --- a/testcases/kernel/containers/pidns/pidns03.c +++ b/testcases/kernel/containers/pidns/pidns03.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Red Hat, Inc. All rights reserved. * Copyright (C) 2022 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and check if procfs mounted folder * belongs to the new pid namespace by looking at /proc/self . */ diff --git a/testcases/kernel/containers/pidns/pidns04.c b/testcases/kernel/containers/pidns/pidns04.c index bed75a08..ff106780 100755 --- a/testcases/kernel/containers/pidns/pidns04.c +++ b/testcases/kernel/containers/pidns/pidns04.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) International Business Machines Corp., 2008 * Copyright (C) 2022 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and check that child container does * not get kill itself with SIGKILL. */ diff --git a/testcases/kernel/containers/pidns/pidns05.c b/testcases/kernel/containers/pidns/pidns05.c index 0e7739aa..b1666f23 100755 --- a/testcases/kernel/containers/pidns/pidns05.c +++ b/testcases/kernel/containers/pidns/pidns05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and create many levels of child * containers. Then kill container init process from parent and check if all * containers have been killed. @@ -24,7 +22,7 @@ static struct tst_clone_args clone_args = { }; static pid_t pid_max; -static void child_func(int *level) +static void child_func(const int level) { pid_t cpid, ppid; @@ -34,15 +32,13 @@ static void child_func(int *level) TST_EXP_EQ_LI(cpid, 1); TST_EXP_EQ_LI(ppid, 0); - if (*level >= MAX_DEPTH) { + if (level >= MAX_DEPTH - 1) { TST_CHECKPOINT_WAKE(0); return; } - (*level)++; - if (!SAFE_CLONE(&clone_args)) { - child_func(level); + child_func(level + 1); return; } @@ -81,14 +77,13 @@ static void setup(void) static void run(void) { int i, status, children; - int level = 0; pid_t pids_new[MAX_DEPTH]; pid_t pids[MAX_DEPTH]; pid_t pid; pid = SAFE_CLONE(&clone_args); if (!pid) { - child_func(&level); + child_func(0); return; } diff --git a/testcases/kernel/containers/pidns/pidns06.c b/testcases/kernel/containers/pidns/pidns06.c index c85a875e..b79a5d40 100755 --- a/testcases/kernel/containers/pidns/pidns06.c +++ b/testcases/kernel/containers/pidns/pidns06.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) International Business Machines Corp., 2008 * Copyright (C) 2022 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and check that parent process can't * be killed from child namespace. */ diff --git a/testcases/kernel/containers/pidns/pidns10.c b/testcases/kernel/containers/pidns/pidns10.c index c2a9094b..ab6a7a4a 100755 --- a/testcases/kernel/containers/pidns/pidns10.c +++ b/testcases/kernel/containers/pidns/pidns10.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) International Business Machines Corp., 2008 * Copyright (C) 2022 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and check that killing subprocesses * from child namespace will raise ESRCH error. */ diff --git a/testcases/kernel/containers/pidns/pidns12.c b/testcases/kernel/containers/pidns/pidns12.c index 1811dbc3..4de4e3d1 100755 --- a/testcases/kernel/containers/pidns/pidns12.c +++ b/testcases/kernel/containers/pidns/pidns12.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and verifies that siginfo->si_pid is * set to 0 if sender (parent process) is not in the receiver's namespace. */ diff --git a/testcases/kernel/containers/pidns/pidns13.c b/testcases/kernel/containers/pidns/pidns13.c index 65fcc444..1ea9f5cd 100755 --- a/testcases/kernel/containers/pidns/pidns13.c +++ b/testcases/kernel/containers/pidns/pidns13.c @@ -9,12 +9,11 @@ */ /*\ - * [Description] - * * The pidns13.c testcase checks container init, for async I/O * triggered by peer namespace process. * * [Algorithm] + * * * create a pipe in parent namespace * * create two PID namespace containers(cinit1 and cinit2) * * in cinit1, set pipe read end to send SIGUSR1 for asynchronous I/O diff --git a/testcases/kernel/containers/pidns/pidns16.c b/testcases/kernel/containers/pidns/pidns16.c index 313b0a09..8867a132 100755 --- a/testcases/kernel/containers/pidns/pidns16.c +++ b/testcases/kernel/containers/pidns/pidns16.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and verifies that siginfo->si_pid is * set to 0 if sender (parent process) sent the signal. Then send signal from * container itself and check if siginfo->si_pid is set to 1. diff --git a/testcases/kernel/containers/pidns/pidns17.c b/testcases/kernel/containers/pidns/pidns17.c index 4633ec14..3a85d967 100755 --- a/testcases/kernel/containers/pidns/pidns17.c +++ b/testcases/kernel/containers/pidns/pidns17.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and spawn many children inside the * container. Then terminate all children and check if they were signaled. */ diff --git a/testcases/kernel/containers/pidns/pidns20.c b/testcases/kernel/containers/pidns/pidns20.c index 91482086..aa008000 100755 --- a/testcases/kernel/containers/pidns/pidns20.c +++ b/testcases/kernel/containers/pidns/pidns20.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag, block SIGUSR1 signal before sending * it from parent and check if it's received once SIGUSR1 signal is unblocked. */ diff --git a/testcases/kernel/containers/pidns/pidns30.c b/testcases/kernel/containers/pidns/pidns30.c index 4a8bc5e2..409b37ec 100755 --- a/testcases/kernel/containers/pidns/pidns30.c +++ b/testcases/kernel/containers/pidns/pidns30.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag, register notification on a posix * mqueue and send a mqueue message from the parent. Then check if signal * notification contains si_pid of the parent. diff --git a/testcases/kernel/containers/pidns/pidns31.c b/testcases/kernel/containers/pidns/pidns31.c index 7312f8bd..a8d73709 100755 --- a/testcases/kernel/containers/pidns/pidns31.c +++ b/testcases/kernel/containers/pidns/pidns31.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag, register notification on a posix * mqueue and send a mqueue message from the child. Then check if signal * notification contains si_pid of the child. diff --git a/testcases/kernel/containers/pidns/pidns32.c b/testcases/kernel/containers/pidns/pidns32.c index 0738369b..a192c128 100755 --- a/testcases/kernel/containers/pidns/pidns32.c +++ b/testcases/kernel/containers/pidns/pidns32.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Clone a process with CLONE_NEWPID flag and check for the maxium amount of * nested containers. */ @@ -23,7 +21,7 @@ static const struct tst_clone_args args = { .flags = CLONE_NEWPID, .exit_signal = SIGCHLD, }; -static int *level; +static tst_atomic_t *level; static pid_t child_func(void) { diff --git a/testcases/kernel/containers/sysvipc/mesgq_nstest.c b/testcases/kernel/containers/sysvipc/mesgq_nstest.c index 4b12c1ce..6c65c550 100755 --- a/testcases/kernel/containers/sysvipc/mesgq_nstest.c +++ b/testcases/kernel/containers/sysvipc/mesgq_nstest.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test SysV IPC message passing through different namespaces. * * [Algorithm] diff --git a/testcases/kernel/containers/sysvipc/msg_comm.c b/testcases/kernel/containers/sysvipc/msg_comm.c index 3762adb0..81bd0963 100755 --- a/testcases/kernel/containers/sysvipc/msg_comm.c +++ b/testcases/kernel/containers/sysvipc/msg_comm.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test SysV IPC message passing through different processes. * * [Algorithm] diff --git a/testcases/kernel/containers/sysvipc/sem_comm.c b/testcases/kernel/containers/sysvipc/sem_comm.c index d8f0956a..ec675262 100755 --- a/testcases/kernel/containers/sysvipc/sem_comm.c +++ b/testcases/kernel/containers/sysvipc/sem_comm.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test SysV IPC semaphore usage between cloned processes. * * [Algorithm] diff --git a/testcases/kernel/containers/sysvipc/sem_nstest.c b/testcases/kernel/containers/sysvipc/sem_nstest.c index 35d55cba..133795f0 100755 --- a/testcases/kernel/containers/sysvipc/sem_nstest.c +++ b/testcases/kernel/containers/sysvipc/sem_nstest.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test SysV IPC semaphore usage between namespaces. * * [Algorithm] diff --git a/testcases/kernel/containers/sysvipc/semtest_2ns.c b/testcases/kernel/containers/sysvipc/semtest_2ns.c index f03b18f7..8c26dfd5 100755 --- a/testcases/kernel/containers/sysvipc/semtest_2ns.c +++ b/testcases/kernel/containers/sysvipc/semtest_2ns.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test SysV IPC semaphore usage between namespaces and processes. * * [Algorithm] diff --git a/testcases/kernel/containers/sysvipc/shm_comm.c b/testcases/kernel/containers/sysvipc/shm_comm.c index e7ba8c8d..30916ded 100755 --- a/testcases/kernel/containers/sysvipc/shm_comm.c +++ b/testcases/kernel/containers/sysvipc/shm_comm.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test if SysV IPC shared memory is properly working between two different * namespaces. * diff --git a/testcases/kernel/containers/sysvipc/shmem_2nstest.c b/testcases/kernel/containers/sysvipc/shmem_2nstest.c index a184cfcb..c984ee72 100755 --- a/testcases/kernel/containers/sysvipc/shmem_2nstest.c +++ b/testcases/kernel/containers/sysvipc/shmem_2nstest.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test if SysV IPC shared memory is properly used between two namespaces. * * [Algorithm] diff --git a/testcases/kernel/containers/sysvipc/shmnstest.c b/testcases/kernel/containers/sysvipc/shmnstest.c index 63ae62aa..aa576eb0 100755 --- a/testcases/kernel/containers/sysvipc/shmnstest.c +++ b/testcases/kernel/containers/sysvipc/shmnstest.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test if SysV IPC shared memory with a specific key is shared between * processes and namespaces. */ diff --git a/testcases/kernel/containers/userns/userns01.c b/testcases/kernel/containers/userns/userns01.c index 6fe0cd63..6ee9fa7d 100755 --- a/testcases/kernel/containers/userns/userns01.c +++ b/testcases/kernel/containers/userns/userns01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that if a user ID has no mapping inside the namespace, user ID and * group ID will be the value defined in the file /proc/sys/kernel/overflowuid * (defaults to 65534) and /proc/sys/kernel/overflowgid (defaults to 65534). A diff --git a/testcases/kernel/containers/userns/userns02.c b/testcases/kernel/containers/userns/userns02.c index 3c8ce213..d71c5397 100755 --- a/testcases/kernel/containers/userns/userns02.c +++ b/testcases/kernel/containers/userns/userns02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that the user ID and group ID, which are inside a container, * can be modified by its parent process. */ diff --git a/testcases/kernel/containers/userns/userns03.c b/testcases/kernel/containers/userns/userns03.c index fca85870..8fdad595 100755 --- a/testcases/kernel/containers/userns/userns03.c +++ b/testcases/kernel/containers/userns/userns03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that /proc/PID/uid_map and /proc/PID/gid_map contains three values * separated by white space: * diff --git a/testcases/kernel/containers/userns/userns04.c b/testcases/kernel/containers/userns/userns04.c index d20041f0..1ca7bf6e 100755 --- a/testcases/kernel/containers/userns/userns04.c +++ b/testcases/kernel/containers/userns/userns04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that if a namespace isn't another namespace's ancestor, the process in * first namespace does not have the CAP_SYS_ADMIN capability in the second * namespace and the setns() call fails. diff --git a/testcases/kernel/containers/userns/userns05.c b/testcases/kernel/containers/userns/userns05.c index e7a00af1..f94e7116 100755 --- a/testcases/kernel/containers/userns/userns05.c +++ b/testcases/kernel/containers/userns/userns05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that if a process created via fork(2) or clone(2) without the * CLONE_NEWUSER flag is a member of the same user namespace as its parent. * diff --git a/testcases/kernel/containers/userns/userns06.c b/testcases/kernel/containers/userns/userns06.c index a270dafd..3a1909ce 100755 --- a/testcases/kernel/containers/userns/userns06.c +++ b/testcases/kernel/containers/userns/userns06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that when a process with non-zero user IDs performs an execve(), * the process's capability sets are cleared. * When a process with zero user IDs performs an execve(), the process's diff --git a/testcases/kernel/containers/userns/userns07.c b/testcases/kernel/containers/userns/userns07.c index 2217a5ed..9e34a3d9 100755 --- a/testcases/kernel/containers/userns/userns07.c +++ b/testcases/kernel/containers/userns/userns07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that the kernel allows at least 32 nested levels of user namespaces. */ diff --git a/testcases/kernel/containers/userns/userns08.c b/testcases/kernel/containers/userns/userns08.c index 72d7f8d1..fbe51c8a 100755 --- a/testcases/kernel/containers/userns/userns08.c +++ b/testcases/kernel/containers/userns/userns08.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Reproducer of CVE-2018-18955; broken uid/gid mapping for nested * user namespaces with >5 ranges * diff --git a/testcases/kernel/containers/utsname/utsname01.c b/testcases/kernel/containers/utsname/utsname01.c index fc5c1a27..a72ad1c3 100644 --- a/testcases/kernel/containers/utsname/utsname01.c +++ b/testcases/kernel/containers/utsname/utsname01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Clone two plain processes and check if both read the same hostname. */ diff --git a/testcases/kernel/containers/utsname/utsname02.c b/testcases/kernel/containers/utsname/utsname02.c index aa90596d..b100d212 100644 --- a/testcases/kernel/containers/utsname/utsname02.c +++ b/testcases/kernel/containers/utsname/utsname02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Clone two plain processes, change hostname in the first one then check if * hostaname has changed inside the second one as well. */ diff --git a/testcases/kernel/containers/utsname/utsname03.c b/testcases/kernel/containers/utsname/utsname03.c index e5a4a56d..0680058e 100644 --- a/testcases/kernel/containers/utsname/utsname03.c +++ b/testcases/kernel/containers/utsname/utsname03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Clone two processes using CLONE_NEWUTS, change hostname from the first * container and check if hostname didn't change inside the second one. */ diff --git a/testcases/kernel/containers/utsname/utsname04.c b/testcases/kernel/containers/utsname/utsname04.c index bf97880b..2bcf6115 100644 --- a/testcases/kernel/containers/utsname/utsname04.c +++ b/testcases/kernel/containers/utsname/utsname04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Drop root privileges, create a container with CLONE_NEWUTS and verify that * we receive a permission error. */ diff --git a/testcases/kernel/controllers/cgroup/cgroup_core01.c b/testcases/kernel/controllers/cgroup/cgroup_core01.c index 2e695dee..11231fbf 100644 --- a/testcases/kernel/controllers/cgroup/cgroup_core01.c +++ b/testcases/kernel/controllers/cgroup/cgroup_core01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * When a task is writing to an fd opened by a different task, the perm check * should use the credentials of the latter task. * diff --git a/testcases/kernel/controllers/cgroup/cgroup_core02.c b/testcases/kernel/controllers/cgroup/cgroup_core02.c index 1872a7df..aba38719 100644 --- a/testcases/kernel/controllers/cgroup/cgroup_core02.c +++ b/testcases/kernel/controllers/cgroup/cgroup_core02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * When a task is writing to an fd opened by a different task, the perm check * should use the cgroup namespace of the latter task. * @@ -32,6 +30,8 @@ #include "tst_safe_file_at.h" #include "lapi/sched.h" +#define STACK_SIZE 65536 + static struct tst_cg_group *cg_child_a, *cg_child_b; static uid_t nobody_uid; @@ -51,7 +51,7 @@ static int lesser_ns_open_thread_fn(void *arg) static void test_lesser_ns_open(void) { int i; - static char stack[65536]; + char *stack; pid_t pid; int status; struct lesser_ns_open_thread_arg targ = { .fds = {0}, .loops = -1}; @@ -63,14 +63,19 @@ static void test_lesser_ns_open(void) SAFE_CG_PRINT(cg_child_a, "cgroup.procs", "0"); SAFE_CG_FCHOWN(cg_child_a, "cgroup.procs", nobody_uid, -1); SAFE_CG_FCHOWN(cg_child_b, "cgroup.procs", nobody_uid, -1); + stack = SAFE_MMAP(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); pid = ltp_clone(CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD, - lesser_ns_open_thread_fn, &targ, 65536, stack); + lesser_ns_open_thread_fn, &targ, STACK_SIZE, stack); + if (pid < 0) { tst_res(TFAIL, "unexpected negative pid %d", pid); exit(1); } SAFE_WAITPID(pid, &status, 0); + SAFE_MUNMAP(stack, STACK_SIZE); + for (i = 0; i < targ.loops; i++) { if (targ.fds[i] < 1) { tst_res(TFAIL, "unexpected negative fd %d", targ.fds[i]); @@ -121,6 +126,7 @@ static struct tst_test test = { .needs_root = 1, .needs_cgroup_ctrls = (const char *const[]){"memory", NULL}, .needs_cgroup_ver = TST_CG_V2, + .needs_cgroup_nsdelegate = 1, .tags = (const struct tst_tag[]) { {"linux-git", "e57457641613"}, {"CVE", "2021-4197"}, diff --git a/testcases/kernel/controllers/cgroup/cgroup_core03.c b/testcases/kernel/controllers/cgroup/cgroup_core03.c index 7d40d47f..846c00f2 100644 --- a/testcases/kernel/controllers/cgroup/cgroup_core03.c +++ b/testcases/kernel/controllers/cgroup/cgroup_core03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This test is copied from kselftest * tools/testing/selftests/cgroup/test_kill.c. * @@ -24,7 +22,7 @@ #define PID_NUM MIN(MAX_PID_NUM, (tst_ncpus_available() + 1)) #define BUF_LEN (20 * PID_NUM) -static int *data_ptr; +static tst_atomic_t *data_ptr; static char *buf; static struct tst_cg_group *cg_child_test_simple; @@ -124,7 +122,7 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .forks_child = 1, - .max_runtime = 20, + .timeout = 20, .needs_cgroup_ctrls = (const char *const []){ "base", NULL }, .needs_cgroup_ver = TST_CG_V2, .needs_checkpoints = 1, diff --git a/testcases/kernel/controllers/cgroup/cgroup_regression_test.sh b/testcases/kernel/controllers/cgroup/cgroup_regression_test.sh index c241a5c4..276231fe 100755 --- a/testcases/kernel/controllers/cgroup/cgroup_regression_test.sh +++ b/testcases/kernel/controllers/cgroup/cgroup_regression_test.sh @@ -352,7 +352,7 @@ test7() return fi - subsys=`tail -n 1 /proc/cgroups | awk '{ print $1 }'` + subsys=$(awk 'END{ print $1 }' /proc/cgroups) # remount to add new subsystems to the hierarchy while [ $i -le 2 ]; do diff --git a/testcases/kernel/controllers/cgroup_fj/cgroup_fj_common.sh b/testcases/kernel/controllers/cgroup_fj/cgroup_fj_common.sh index 6d558653..153d351d 100755 --- a/testcases/kernel/controllers/cgroup_fj/cgroup_fj_common.sh +++ b/testcases/kernel/controllers/cgroup_fj/cgroup_fj_common.sh @@ -76,6 +76,14 @@ common_cleanup() [ -d "$start_path" ] && find "$start_path" -depth -type d -exec rmdir '{}' \; cgroup_cleanup + + if [ "$cgroup_version" = "2" ]; then + case "$subsystem" in + cpu|io|memory|pids) + :;; + *) ROD echo "-$subsystem" \> "/sys/fs/cgroup/cgroup.subtree_control";; + esac + fi } . cgroup_lib.sh diff --git a/testcases/kernel/controllers/cgroup_lib.sh b/testcases/kernel/controllers/cgroup_lib.sh index 9e59221a..50d0cd34 100755 --- a/testcases/kernel/controllers/cgroup_lib.sh +++ b/testcases/kernel/controllers/cgroup_lib.sh @@ -119,12 +119,12 @@ cgroup_require() ret=$? if [ $ret -eq 32 ]; then - tst_brk TCONF "'tst_cgctl require' exited. Controller is probably not available?" + tst_brk TCONF "'tst_cgctl require $ctrl' failed. $ctrl controller not available?" return $ret fi if [ $ret -ne 0 ]; then - tst_brk TBROK "'tst_cgctl require' exited" + tst_brk TBROK "'tst_cgctl require $ctrl' failed. LTP missing $ctrl controller support?" return $ret fi diff --git a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c index a870118f..0d016b58 100755 --- a/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c +++ b/testcases/kernel/controllers/cgroup_xattr/cgroup_xattr.c @@ -151,11 +151,8 @@ void setup(int argc, char *argv[]) tst_brkm(TCONF, NULL, "Kernel doesn't support cgroups"); for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { - if (!strcmp(tkeys[i].name, "security.")) { - tkeys[i].good = tst_kvercmp(3, 15, 0) < 0; - } else if (!strcmp(tkeys[i].name, "trusted.")) { + if (!strcmp(tkeys[i].name, "trusted.")) tkeys[i].good = tst_kvercmp(4, 5, 0) < 0; - } } int value_size = DEFAULT_VALUE_SIZE; @@ -269,12 +266,7 @@ int mount_cgroup(void) * additional "xattr" option. In that case, mount will succeed, * but xattr won't be supported in the new mount anyway. * Should be removed as soon as a fix committed to upstream. - * - * But not applicable for kernels >= 3.15 where xattr supported - * natively. */ - if (hier != 0 && tst_kvercmp(3, 15, 0) < 0) - continue; int i, found = 0; for (i = 0; i < cgrp_opt_num; ++i) { diff --git a/testcases/kernel/controllers/cpuset/cpuset_funcs.sh b/testcases/kernel/controllers/cpuset/cpuset_funcs.sh index 0cfa0c17..f43518c7 100755 --- a/testcases/kernel/controllers/cpuset/cpuset_funcs.sh +++ b/testcases/kernel/controllers/cpuset/cpuset_funcs.sh @@ -165,8 +165,7 @@ setup() mount -t cgroup -o cpuset cpuset "$CPUSET" 2> /dev/null if [ $? -ne 0 ]; then cleanup - tst_brkm TFAIL "Could not mount cgroup filesystem with"\ - " cpuset on $CPUSET..Exiting test" + tst_brkm TCONF "Could not mount cgroup filesystem with cpuset on $CPUSET" fi CHILDREN_VALUE="`cat $CLONE_CHILDREN`" @@ -196,8 +195,7 @@ cleanup() done < "$subdir/tasks" rmdir "$subdir" if [ $? -ne 0 ]; then - tst_brkm TFAIL "Couldn't remove subdir - " - "$subdir in the cpuset" + tst_brkm TFAIL "Couldn't remove subdir - $subdir in the cpuset" fi done diff --git a/testcases/kernel/controllers/cpuset/cpuset_memory_spread_test/cpuset_memory_spread_testset.sh b/testcases/kernel/controllers/cpuset/cpuset_memory_spread_test/cpuset_memory_spread_testset.sh index f7230a4e..21e67179 100755 --- a/testcases/kernel/controllers/cpuset/cpuset_memory_spread_test/cpuset_memory_spread_testset.sh +++ b/testcases/kernel/controllers/cpuset/cpuset_memory_spread_test/cpuset_memory_spread_testset.sh @@ -347,6 +347,7 @@ if [ $? -ne 0 ]; then tst_brkm TFAIL "Creating DATAFILE failed." fi +rm -f $FIFO mkfifo $FIFO if [ $? -ne 0 ]; then rm -f DATAFILE diff --git a/testcases/kernel/controllers/cpuset/cpuset_memory_test/cpuset_memory_testset.sh b/testcases/kernel/controllers/cpuset/cpuset_memory_test/cpuset_memory_testset.sh index c1e7cea8..e81d2229 100755 --- a/testcases/kernel/controllers/cpuset/cpuset_memory_test/cpuset_memory_testset.sh +++ b/testcases/kernel/controllers/cpuset/cpuset_memory_test/cpuset_memory_testset.sh @@ -23,7 +23,7 @@ ################################################################################ export TCID="cpuset_memory" -export TST_TOTAL=18 +export TST_TOTAL=17 export TST_COUNT=1 . cpuset_funcs.sh @@ -181,7 +181,7 @@ test6() save_nr_hugepages=$(cat /proc/sys/vm/nr_hugepages) echo $((2*$nr_mems)) > /proc/sys/vm/nr_hugepages - cpuset_memory_test --mmap-file --hugepage -s $HUGEPAGESIZE >"$MEMORY_RESULT" & + cpuset_memory_test --shm --hugepage -s $HUGEPAGESIZE --key=7 >"$MEMORY_RESULT" & simple_getresult $! "$CPUSET/0" umount /hugetlb @@ -208,45 +208,6 @@ test7() return 1 fi - check_hugetlbfs - if [ $? -eq 0 ]; then - tst_resm TCONF "This system don't support hugetlbfs" - return 0 - fi - - mkdir /hugetlb - mount -t hugetlbfs none /hugetlb - - save_nr_hugepages=$(cat /proc/sys/vm/nr_hugepages) - echo $((2*$nr_mems)) > /proc/sys/vm/nr_hugepages - - cpuset_memory_test --shm --hugepage -s $HUGEPAGESIZE --key=7 >"$MEMORY_RESULT" & - simple_getresult $! "$CPUSET/0" - - umount /hugetlb - rmdir /hugetlb - - echo $save_nr_hugepages > /proc/sys/vm/nr_hugepages - if [ $(cat /proc/sys/vm/nr_hugepages) -ne $save_nr_hugepages ]; then - tst_resm TFAIL "can't restore nr_hugepages(nr_hugepages = $save_nr_hugepages)." - return 1 - fi - - if [ "$node" != "0" ]; then - tst_resm TFAIL "allocate memory on the Node#$node(Expect: Node#0)." - return 1 - fi -} - -test8() -{ - cpuset_set "$CPUSET/0" "$cpu_of_node0" "0" "0" 2> $CPUSET_TMP/stderr - if [ $? -ne 0 ]; then - cpuset_log_error $CPUSET_TMP/stderr - tst_resm TFAIL "set general group parameter failed." - return 1 - fi - cpuset_memory_test --mmap-anon >"$MEMORY_RESULT" & simple_getresult $! "$CPUSET/0" if [ "$node" != "0" ]; then @@ -255,7 +216,7 @@ test8() fi } -test9() +test8() { cpuset_set "$CPUSET/0" "$cpu_of_node0" "1" "0" 2> $CPUSET_TMP/stderr if [ $? -ne 0 ]; then @@ -291,7 +252,7 @@ talk2memory_test_for_case_10_11() wait $1 } -test10() +test9() { cpuset_set "$CPUSET/1" "$cpus_all" "0" "0" 2> $CPUSET_TMP/stderr if [ $? -ne 0 ]; then @@ -329,7 +290,7 @@ test10() fi } -test11() +test10() { cpuset_set "$CPUSET/1" "$cpus_all" "0" "0" 2> $CPUSET_TMP/stderr if [ $? -ne 0 ]; then @@ -395,7 +356,7 @@ talk2memory_test_for_case_12_13() } -test12() +test11() { cpuset_set "$CPUSET/0" "$cpu_of_node0" "1" "0" 2> $CPUSET_TMP/stderr if [ $? -ne 0 ]; then @@ -423,7 +384,7 @@ test12() } -test13() +test12() { cpuset_set "$CPUSET/0" "$cpu_of_node0" "1" "0" 2> $CPUSET_TMP/stderr if [ $? -ne 0 ]; then @@ -479,6 +440,54 @@ get_the_second() ) } +test13() +{ + cpuset_set "$CPUSET/1" "$cpu_of_node0" "0" "0" 2> $CPUSET_TMP/stderr + if [ $? -ne 0 ]; then + cpuset_log_error $CPUSET_TMP/stderr + tst_resm TFAIL "set general group1's parameter failed." + return 1 + fi + + cpuset_set "$CPUSET/2" "$cpu_of_node0" "1" "0" 2> $CPUSET_TMP/stderr + if [ $? -ne 0 ]; then + cpuset_log_error $CPUSET_TMP/stderr + tst_resm TFAIL "set general group2's parameter failed." + return 1 + fi + + cpuset_memory_test --thread --mmap-anon >"$MEMORY_RESULT" & + { + local testpid=$! + sleep 1 + local testtid=$(get_the_second $testpid) + + echo $testpid > "$CPUSET/1/tasks" + /bin/kill -s SIGUSR1 $testpid + + echo $testtid > "$CPUSET/2/tasks" + sleep 1 + /bin/kill -s SIGUSR2 $testpid + sleep 1 + /bin/kill -s SIGINT $testpid + wait $testpid + } + + { + read node0 + read node1 + } < "$MEMORY_RESULT" + + if [ "$node0" != "0" ]; then + tst_resm TFAIL "Thread1 allocated memory on the Node#$node0(Expect: Node#0)." + return 1 + fi + if [ "$node1" != "1" ]; then + tst_resm TFAIL "Thread2 allocated memory on the Node#$node1(Expect: Node#1)." + return 1 + fi +} + test14() { cpuset_set "$CPUSET/1" "$cpu_of_node0" "0" "0" 2> $CPUSET_TMP/stderr @@ -495,6 +504,14 @@ test14() return 1 fi + echo 1 > "$CPUSET/2/cpuset.memory_migrate" 2> $CPUSET_TMP/stderr + if [ $? -ne 0 ]; then + cpuset_log_error $CPUSET_TMP/stderr + tst_resm TFAIL "set general group2's memory_migrate failed." + return 1 + fi + + cpuset_memory_test --thread --mmap-anon >"$MEMORY_RESULT" & { local testpid=$! @@ -551,62 +568,6 @@ test15() fi - cpuset_memory_test --thread --mmap-anon >"$MEMORY_RESULT" & - { - local testpid=$! - sleep 1 - local testtid=$(get_the_second $testpid) - - echo $testpid > "$CPUSET/1/tasks" - /bin/kill -s SIGUSR1 $testpid - - echo $testtid > "$CPUSET/2/tasks" - sleep 1 - /bin/kill -s SIGUSR2 $testpid - sleep 1 - /bin/kill -s SIGINT $testpid - wait $testpid - } - - { - read node0 - read node1 - } < "$MEMORY_RESULT" - - if [ "$node0" != "0" ]; then - tst_resm TFAIL "Thread1 allocated memory on the Node#$node0(Expect: Node#0)." - return 1 - fi - if [ "$node1" != "1" ]; then - tst_resm TFAIL "Thread2 allocated memory on the Node#$node1(Expect: Node#1)." - return 1 - fi -} - -test16() -{ - cpuset_set "$CPUSET/1" "$cpu_of_node0" "0" "0" 2> $CPUSET_TMP/stderr - if [ $? -ne 0 ]; then - cpuset_log_error $CPUSET_TMP/stderr - tst_resm TFAIL "set general group1's parameter failed." - return 1 - fi - - cpuset_set "$CPUSET/2" "$cpu_of_node0" "1" "0" 2> $CPUSET_TMP/stderr - if [ $? -ne 0 ]; then - cpuset_log_error $CPUSET_TMP/stderr - tst_resm TFAIL "set general group2's parameter failed." - return 1 - fi - - echo 1 > "$CPUSET/2/cpuset.memory_migrate" 2> $CPUSET_TMP/stderr - if [ $? -ne 0 ]; then - cpuset_log_error $CPUSET_TMP/stderr - tst_resm TFAIL "set general group2's memory_migrate failed." - return 1 - fi - - cpuset_memory_test --thread --mmap-anon >"$MEMORY_RESULT" & { local testpid=$! @@ -650,7 +611,7 @@ test16() fi } -test17() +test16() { cpuset_set "$CPUSET/1" "$cpu_of_node0" "1" "0" 2> $CPUSET_TMP/stderr if [ $? -ne 0 ]; then @@ -725,7 +686,7 @@ test17() fi } -test18() +test17() { cpuset_set "$CPUSET/1" "$cpu_of_node0" "1" "0" 2> $CPUSET_TMP/stderr if [ $? -ne 0 ]; then diff --git a/testcases/kernel/controllers/cpuset/cpuset_regression_test.sh b/testcases/kernel/controllers/cpuset/cpuset_regression_test.sh index a5757309..38eb2882 100755 --- a/testcases/kernel/controllers/cpuset/cpuset_regression_test.sh +++ b/testcases/kernel/controllers/cpuset/cpuset_regression_test.sh @@ -15,7 +15,6 @@ TST_CLEANUP=cleanup TST_TESTFUNC=do_test TST_NEEDS_ROOT=1 TST_NEEDS_TMPDIR=1 -TST_MIN_KVER="3.18" LOCAL_MOUNTPOINT="cpuset_test" BACKUP_DIRECTORY="cpuset_backup" @@ -190,21 +189,27 @@ do_test() ROD_SILENT mkdir ${root_cpuset_dir}/testdir # Creat an exclusive cpuset. - echo 1 > ${root_cpuset_dir}/testdir/${cpu_exclusive} - [ $? -ne 0 ] && tst_brk TFAIL "'echo 1 > ${root_cpuset_dir}/testdir/${cpu_exclusive}' failed" + if ! echo 1 > ${root_cpuset_dir}/testdir/${cpu_exclusive}; then + tst_res TFAIL "'echo 1 > ${root_cpuset_dir}/testdir/${cpu_exclusive}' failed" + return + fi cpu_exclusive_tmp=$(cat ${root_cpuset_dir}/testdir/${cpu_exclusive}) if [ "${cpu_exclusive_tmp}" != "1" ]; then - tst_brk TFAIL "${cpu_exclusive} is '${cpu_exclusive_tmp}', expected '1'" + tst_res TFAIL "${cpu_exclusive} is '${cpu_exclusive_tmp}', expected '1'" + return fi # This may trigger the kernel crash - echo 0 > ${root_cpuset_dir}/testdir/${cpus} - [ $? -ne 0 ] && tst_brk TFAIL "'echo 0 > ${root_cpuset_dir}/testdir/${cpus}' failed" + if ! echo 0 > ${root_cpuset_dir}/testdir/${cpus}; then + tst_res TFAIL "'echo 0 > ${root_cpuset_dir}/testdir/${cpus}' failed" + return + fi cpus_value=$(cat ${root_cpuset_dir}/testdir/${cpus}) if [ "${cpus_value}" != "0" ]; then - tst_brk TFAIL "${cpus} is '${cpus_value}', expected '0'" + tst_res TFAIL "${cpus} is '${cpus_value}', expected '0'" + return fi tst_res TPASS "Bug is not reproducible" diff --git a/testcases/kernel/controllers/freezer/vfork.c b/testcases/kernel/controllers/freezer/vfork.c index 0b25e90c..9f4359bf 100755 --- a/testcases/kernel/controllers/freezer/vfork.c +++ b/testcases/kernel/controllers/freezer/vfork.c @@ -29,6 +29,7 @@ * until vfork returns. This can delay delivery of signals to the parent * process, even delay or stop system suspend. */ + #include #include #include @@ -39,9 +40,8 @@ #include #include #include +#include #include "test.h" -#include "config.h" -#include "../../syscalls/ptrace/ptrace.h" #define str_expand(s) str(s) #define str(s) #s diff --git a/testcases/kernel/controllers/io/io_control01.c b/testcases/kernel/controllers/io/io_control01.c index 69119688..93e6aed8 100644 --- a/testcases/kernel/controllers/io/io_control01.c +++ b/testcases/kernel/controllers/io/io_control01.c @@ -1,10 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2022 SUSE LLC */ /*\ - * - * [Description] * * Perform some I/O on a file and check if at least some of it is * recorded by the I/O controller. @@ -64,11 +62,10 @@ static void run(void) if (convs < 2) continue; - tst_res(TINFO, "Found %u:%u in io.stat", dev_major, dev_minor); - - if (start.mjr == dev_major || start.mnr == dev_minor) + if (start.mjr == dev_major && start.mnr == dev_minor) { + tst_res(TINFO, "Found %u:%u in io.stat", dev_major, dev_minor); break; - + } line = strtok_r(NULL, "\n", &buf_ptr); } diff --git a/testcases/kernel/controllers/libcontrollers/libcontrollers.c b/testcases/kernel/controllers/libcontrollers/libcontrollers.c index e9917271..18f7257c 100755 --- a/testcases/kernel/controllers/libcontrollers/libcontrollers.c +++ b/testcases/kernel/controllers/libcontrollers/libcontrollers.c @@ -39,11 +39,9 @@ char fullpath[PATH_MAX]; int FLAG; volatile int timer_expired = 0; int retval; -unsigned int num_line; unsigned int current_shares; unsigned int total_shares; unsigned int *shares_pointer; -char target[LINE_MAX]; struct dirent *dir_pointer; /* @@ -133,6 +131,9 @@ int read_file(char *filepath, int action, unsigned int *value) int num_line = 0; FILE *fp; int tmp; + size_t len; + char *target = NULL; + switch (action) { case GET_SHARES: tmp = read_shares_file(filepath); @@ -147,8 +148,9 @@ int read_file(char *filepath, int action, unsigned int *value) error_function("Could not open file", filepath); return -1; } - while (fgets(target, LINE_MAX, fp) != NULL) + while (getline(&target, &len, fp) != -1) num_line++; + free(target); *value = (unsigned int)num_line; if (fclose(fp)) { error_function("Could not close file", filepath); diff --git a/testcases/kernel/controllers/libcontrollers/libcontrollers.h b/testcases/kernel/controllers/libcontrollers/libcontrollers.h index 7d7b8324..54874322 100755 --- a/testcases/kernel/controllers/libcontrollers/libcontrollers.h +++ b/testcases/kernel/controllers/libcontrollers/libcontrollers.h @@ -46,11 +46,9 @@ extern char fullpath[PATH_MAX]; extern int FLAG; extern volatile int timer_expired; extern int retval; -extern unsigned int num_line; extern unsigned int current_shares; extern unsigned int total_shares; extern unsigned int *shares_pointer; -extern char target[LINE_MAX]; extern struct dirent *dir_pointer; enum{ diff --git a/testcases/kernel/controllers/memcg/control/memcg_control_test.sh b/testcases/kernel/controllers/memcg/control/memcg_control_test.sh index 68287a70..79b3a02a 100755 --- a/testcases/kernel/controllers/memcg/control/memcg_control_test.sh +++ b/testcases/kernel/controllers/memcg/control/memcg_control_test.sh @@ -12,7 +12,6 @@ TST_NEEDS_TMPDIR=1 PAGE_SIZE=$(tst_getconf PAGESIZE) -TOT_MEM_LIMIT=$PAGE_SIZE ACTIVE_MEM_LIMIT=$PAGE_SIZE PROC_MEM=$((PAGE_SIZE * 2)) @@ -50,13 +49,22 @@ test1() # If the kernel is built without swap, the $memsw_memory_limit file is missing if [ -e "$test_dir/$memsw_memory_limit" ]; then - ROD echo "$TOT_MEM_LIMIT" \> "$test_dir/$memsw_memory_limit" + if [ "$cgroup_version" = "2" ]; then + # v2 does not have a combined memsw limit like v1. + # Disable swapping in v2 so all pages get acccounted to the non-swap counter. + SWAP_LIMIT=0 + else + # Swapping cannot be disabled via memsw.limit_in_bytes in v1. + # Apply a memsw limit in v1 to capture any swapped pages + SWAP_LIMIT=$ACTIVE_MEM_LIMIT + fi + ROD echo "$SWAP_LIMIT" \> "$test_dir/$memsw_memory_limit" fi KILLED_CNT=0 test_proc_kill - if [ $PROC_MEM -gt $TOT_MEM_LIMIT ] && [ $KILLED_CNT -eq 0 ]; then + if [ $KILLED_CNT -eq 0 ]; then tst_res TFAIL "Test #1: failed" else tst_res TPASS "Test #1: passed" diff --git a/testcases/kernel/controllers/memcg/functional/memcg_subgroup_charge.sh b/testcases/kernel/controllers/memcg/functional/memcg_subgroup_charge.sh index 9bcc0125..3b731142 100755 --- a/testcases/kernel/controllers/memcg/functional/memcg_subgroup_charge.sh +++ b/testcases/kernel/controllers/memcg/functional/memcg_subgroup_charge.sh @@ -33,8 +33,8 @@ test_subgroup() fi echo $MEMCG_PROCESS_PID > tasks - signal_memcg_process $MEM_TO_ALLOC - check_mem_stat "rss" $MEM_TO_ALLOC + signal_memcg_process $MIN_CHARGED + check_mem_stat "rss" $MIN_CHARGED $MEM_TO_ALLOC cd subgroup echo $MEMCG_PROCESS_PID > tasks @@ -66,5 +66,6 @@ test3() # Allocate memory bigger than per-cpu kernel memory MEM_TO_ALLOC=$((PAGESIZES * 2)) +MIN_CHARGED=$((2 * (PAGESIZES - 1))) tst_run diff --git a/testcases/kernel/controllers/memcg/memcontrol01.c b/testcases/kernel/controllers/memcg/memcontrol01.c index 935b9777..f1f5a9df 100755 --- a/testcases/kernel/controllers/memcg/memcontrol01.c +++ b/testcases/kernel/controllers/memcg/memcontrol01.c @@ -1,7 +1,5 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /*\ - * - * [Description] * * Conversion of the first kself test in cgroup/test_memcontrol.c. * This test creates two nested cgroups with and without enabling the diff --git a/testcases/kernel/controllers/memcg/memcontrol02.c b/testcases/kernel/controllers/memcg/memcontrol02.c index 1656176b..101b4e1b 100755 --- a/testcases/kernel/controllers/memcg/memcontrol02.c +++ b/testcases/kernel/controllers/memcg/memcontrol02.c @@ -1,11 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /*\ - * - * [Description] * * Conversion of second kself test in cgroup/test_memcontrol.c. * * Original description: + * * "This test creates a memory cgroup, allocates some anonymous memory * and some pagecache and check memory.current and some memory.stat * values." @@ -14,11 +13,11 @@ * file in V2. Besides error reporting, this test differs from the * kselftest in the following ways: * - * . It supports V1. - * . It writes instead of reads to fill the page cache. Because no + * - It supports V1. + * - It writes instead of reads to fill the page cache. Because no * pages were allocated on tmpfs. - * . It runs on most filesystems available - * . On EXFAT and extN we change the margin of error between all and file + * - It runs on most filesystems available + * - On EXFAT and extN we change the margin of error between all and file * memory to 50%. Because these allocate non-page-cache memory during writes. */ #define _GNU_SOURCE @@ -134,7 +133,7 @@ static struct tst_test test = { .tcnt = 2, .test = test_memcg_current, .mount_device = 1, - .dev_min_size = 256, + .dev_min_size = 300, .mntpoint = TMPDIR, .all_filesystems = 1, .forks_child = 1, diff --git a/testcases/kernel/controllers/memcg/memcontrol03.c b/testcases/kernel/controllers/memcg/memcontrol03.c index bc726f39..d2e489ad 100644 --- a/testcases/kernel/controllers/memcg/memcontrol03.c +++ b/testcases/kernel/controllers/memcg/memcontrol03.c @@ -1,7 +1,5 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /*\ - * - * [Description] * * Conversion of the third kself test in cgroup/test_memcontrol.c. * @@ -96,17 +94,23 @@ static void cleanup_sub_groups(void) } static void alloc_anon_in_child(const struct tst_cg_group *const cg, - const size_t size, const int expect_oom) + size_t size, const int expect_oom) { int status; const pid_t pid = SAFE_FORK(); + size_t cgmem; if (!pid) { SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", getpid()); + SAFE_CG_SCANF(cg, "memory.current", "%zu", &cgmem); + size = size > cgmem ? size - cgmem : 0; tst_res(TINFO, "Child %d in %s: Allocating anon: %"PRIdPTR, getpid(), tst_cg_group_name(cg), size); - alloc_anon(size); + + if (size) + alloc_anon(size); + exit(0); } @@ -130,9 +134,10 @@ static void alloc_anon_in_child(const struct tst_cg_group *const cg, } static void alloc_pagecache_in_child(const struct tst_cg_group *const cg, - const size_t size) + size_t size) { const pid_t pid = SAFE_FORK(); + size_t cgmem; if (pid) { TST_CHECKPOINT_WAIT(CHILD_IDLE); @@ -140,10 +145,16 @@ static void alloc_pagecache_in_child(const struct tst_cg_group *const cg, } SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", getpid()); + SAFE_CG_SCANF(cg, "memory.current", "%zu", &cgmem); + size = size > cgmem ? size - cgmem : 0; tst_res(TINFO, "Child %d in %s: Allocating pagecache: %"PRIdPTR, getpid(), tst_cg_group_name(cg), size); - alloc_pagecache(fd, size); + + if (size) + alloc_pagecache(fd, size); + + SAFE_FSYNC(fd); TST_CHECKPOINT_WAKE(CHILD_IDLE); TST_CHECKPOINT_WAIT(TEST_DONE); @@ -239,7 +250,6 @@ static struct tst_test test = { .cleanup = cleanup, .test_all = test_memcg_min, .mount_device = 1, - .dev_min_size = 256, .mntpoint = TMPDIR, .all_filesystems = 1, .skip_filesystems = (const char *const[]){ diff --git a/testcases/kernel/controllers/memcg/memcontrol04.c b/testcases/kernel/controllers/memcg/memcontrol04.c index c963a1cd..8184dcdf 100644 --- a/testcases/kernel/controllers/memcg/memcontrol04.c +++ b/testcases/kernel/controllers/memcg/memcontrol04.c @@ -1,7 +1,5 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /*\ - * - * [Description] * * Conversion of the forth kself test in cgroup/test_memcontrol.c. * @@ -209,10 +207,14 @@ static void test_memcg_low(void) if (i < E) { TST_EXP_EXPR(low > 0, - "(%c low events=%ld) > 0", id, low); - } else { + "(%c low events=%ld) > 0", id, low); + } else if (i == E) { TST_EXP_EXPR(low == 0, - "(%c low events=%ld) == 0", id, low); + "(%c low events=%ld) == 0", id, low); + } else if (!tst_cg_memory_recursiveprot(leaf_cg[F])) { + /* dont not check F when recursive_protection enabled */ + TST_EXP_EXPR(low == 0, + "(%c low events=%ld) == 0", id, low); } } @@ -232,7 +234,6 @@ static struct tst_test test = { .cleanup = cleanup, .test_all = test_memcg_low, .mount_device = 1, - .dev_min_size = 256, .mntpoint = TMPDIR, .all_filesystems = 1, .skip_filesystems = (const char *const[]){ diff --git a/testcases/kernel/controllers/memcg/memcontrol_common.h b/testcases/kernel/controllers/memcg/memcontrol_common.h index adb6fafb..e39e455d 100644 --- a/testcases/kernel/controllers/memcg/memcontrol_common.h +++ b/testcases/kernel/controllers/memcg/memcontrol_common.h @@ -1,4 +1,7 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef MEMCONTROL_COMMON_H__ +#define MEMCONTROL_COMMON_H__ #include #include @@ -45,3 +48,5 @@ static inline void alloc_anon(const size_t size) free(buf); } + +#endif /* MEMCONTROL_COMMON_H__ */ diff --git a/testcases/kernel/controllers/memcg/regression/memcg_test_3.c b/testcases/kernel/controllers/memcg/regression/memcg_test_3.c index f29c2bea..31b1b5a8 100755 --- a/testcases/kernel/controllers/memcg/regression/memcg_test_3.c +++ b/testcases/kernel/controllers/memcg/regression/memcg_test_3.c @@ -21,6 +21,7 @@ static volatile int sigcounter; static struct tst_cg_group *test_cg; +static pid_t ppid; static void sighandler(int sig LTP_ATTRIBUTE_UNUSED) { @@ -29,8 +30,8 @@ static void sighandler(int sig LTP_ATTRIBUTE_UNUSED) static void do_child(void) { - while (1) - SAFE_KILL(getppid(), SIGUSR1); + while (getppid() == ppid) + SAFE_KILL(ppid, SIGUSR1); exit(0); } @@ -40,6 +41,7 @@ static void do_test(void) pid_t cpid; SAFE_SIGNAL(SIGUSR1, sighandler); + ppid = getpid(); cpid = SAFE_FORK(); if (cpid == 0) diff --git a/testcases/kernel/controllers/test_controllers.sh b/testcases/kernel/controllers/test_controllers.sh index 7aa974ff..774feef3 100755 --- a/testcases/kernel/controllers/test_controllers.sh +++ b/testcases/kernel/controllers/test_controllers.sh @@ -37,18 +37,19 @@ # # ################################################################################## +if mount | grep -w "type cgroup2" > /dev/null 2>&1; then + tst_brkm TCONF "" "test_controllers.sh: V1 controller required, but mounted on V2" + exit 32 +fi + if [ -f /proc/cgroups ] then CPU_CONTROLLER=`grep -w cpu /proc/cgroups | cut -f1`; CPU_CONTROLLER_VALUE=`grep -w cpu /proc/cgroups | cut -f4`; MEM_CONTROLLER=`grep -w memory /proc/cgroups | cut -f1`; MEM_CONTROLLER_VALUE=`grep -w memory /proc/cgroups | cut -f4`; - IOTHROTTLE_CONTROLLER=`grep -w blockio /proc/cgroups | cut -f1`; - IOTHROTTLE_CONTROLLER_VALUE=`grep -w blockio /proc/cgroups | cut -f4`; FREEZER=`grep -w freezer /proc/cgroups | cut -f1`; FREEZER_VALUE=`grep -w freezer /proc/cgroups | cut -f4`; - CPUACCOUNT_CONTROLLER=`grep -w cpuacct /proc/cgroups | cut -f1` - CPUACCOUNT_CONTROLLER_VALUE=`grep -w cpuacct /proc/cgroups | cut -f4` if [ "$CPU_CONTROLLER" = "cpu" ] && [ "$CPU_CONTROLLER_VALUE" = "1" ] then @@ -82,15 +83,6 @@ then echo "Skipping all memory controller testcases...."; fi - if [ "$IOTHROTTLE_CONTROLLER" = "blockio" ] && [ "$IOTHROTTLE_CONTROLLER_VALUE" = "1" ] - then - $LTPROOT/testcases/bin/run_io_throttle_test.sh; - else - echo "CONTROLLERS TESTCASES: WARNING"; - echo "Either Kernel does not support for io controller or functionality is not enabled"; - echo "Skipping all block device I/O throttling testcases...."; - fi - if [ "$FREEZER" = "freezer" ] && [ "$FREEZER_VALUE" = "1" ] then "$LTPROOT/testcases/bin/run_freezer.sh" @@ -100,16 +92,6 @@ then echo "Kernel does not support freezer controller"; echo "Skipping all freezer testcases...."; fi - if [ "$CPUACCOUNT_CONTROLLER" = "cpuacct" ] && [ "$CPUACCOUNT_CONTROLLER_VALUE" = "1" ] - then - $LTPROOT/testcases/bin/run_cpuacct_test.sh 1; - $LTPROOT/testcases/bin/run_cpuacct_test.sh 2; - else - echo "Could not start cpu accounting controller test"; - echo "Either Kernel does not support for cpu accounting controller or functionality is not enabled"; - echo "usage: run_cpuacct_test.sh $TEST_NUM "; - echo "Skipping the cpu accounting controller test..."; - fi else echo "CONTROLLERS TESTCASES: WARNING" echo "Kernel does not support any controller"; diff --git a/testcases/kernel/crypto/af_alg01.c b/testcases/kernel/crypto/af_alg01.c index 7cefe594..2100b369 100755 --- a/testcases/kernel/crypto/af_alg01.c +++ b/testcases/kernel/crypto/af_alg01.c @@ -21,6 +21,7 @@ static void test_with_hash_alg(const char *hash_algname) { char hmac_algname[64]; char key[4096] = { 0 }; + int ret; if (!tst_have_alg("hash", hash_algname)) return; @@ -30,7 +31,9 @@ static void test_with_hash_alg(const char *hash_algname) return; sprintf(hmac_algname, "hmac(hmac(%s))", hash_algname); - if (tst_try_alg("hash", hmac_algname) != ENOENT) { + + ret = tst_try_alg("hash", hmac_algname); + if (ret != ENOENT && ret != EINVAL) { int algfd; tst_res(TFAIL, "instantiated nested hmac algorithm ('%s')!", diff --git a/testcases/kernel/crypto/af_alg02.c b/testcases/kernel/crypto/af_alg02.c index 40d07ca9..0fc24114 100755 --- a/testcases/kernel/crypto/af_alg02.c +++ b/testcases/kernel/crypto/af_alg02.c @@ -77,7 +77,7 @@ static void run(void) static struct tst_test test = { .test_all = run, - .max_runtime = 20, + .runtime = 20, .needs_checkpoints = 1, .tags = (const struct tst_tag[]) { {"linux-git", "ecaaab564978"}, diff --git a/testcases/kernel/crypto/af_alg03.c b/testcases/kernel/crypto/af_alg03.c index bb8d480e..161515ad 100755 --- a/testcases/kernel/crypto/af_alg03.c +++ b/testcases/kernel/crypto/af_alg03.c @@ -15,10 +15,13 @@ static void run(void) { + int ret; + tst_require_alg("aead", "rfc7539(chacha20,poly1305)"); tst_require_alg("hash", "sha256"); - if (tst_try_alg("aead", "rfc7539(chacha20,sha256)") != ENOENT) { + ret = tst_try_alg("aead", "rfc7539(chacha20,sha256)"); + if (ret != ENOENT && ret != EINVAL) { tst_res(TFAIL, "instantiated rfc7539 template with wrong digest size"); } else { diff --git a/testcases/kernel/crypto/af_alg07.c b/testcases/kernel/crypto/af_alg07.c index 9c251663..b680efba 100755 --- a/testcases/kernel/crypto/af_alg07.c +++ b/testcases/kernel/crypto/af_alg07.c @@ -125,7 +125,8 @@ static struct tst_test test = { .cleanup = cleanup, .min_kver = "4.10.0", .min_cpus = 2, - .max_runtime = 150, + .runtime = 150, + .min_runtime = 2, .taint_check = TST_TAINT_W | TST_TAINT_D, .tags = (const struct tst_tag[]) { {"linux-git", "ff7b11aa481f"}, diff --git a/testcases/kernel/crypto/crypto_user01.c b/testcases/kernel/crypto/crypto_user01.c index 47bf9f0d..6f6036ae 100755 --- a/testcases/kernel/crypto/crypto_user01.c +++ b/testcases/kernel/crypto/crypto_user01.c @@ -17,7 +17,6 @@ #include "tst_test.h" #include "tst_crypto.h" -#include "tst_netlink.h" /* * include after (via tst_test.h), to work around dependency bug @@ -25,11 +24,11 @@ */ #include -static struct tst_crypto_session ses = TST_CRYPTO_SESSION_INIT; +static struct tst_netlink_context *ctx; static void setup(void) { - tst_crypto_open(&ses); + ctx = NETLINK_CREATE_CONTEXT(NETLINK_CRYPTO); } static void do_check_for_leaks(const char *name, const char *value, size_t vlen) @@ -131,25 +130,20 @@ static void validate_one_alg(const struct nlmsghdr *nh) } } -static void validate_alg_list(const void *buf, size_t remaining) +static void validate_alg_list(const struct tst_netlink_message *msg) { - const struct nlmsghdr *nh; - - for (nh = buf; NLMSG_OK(nh, remaining); - nh = NLMSG_NEXT(nh, remaining)) { - 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); - } - if (nh->nlmsg_type == NLMSG_DONE) + for (; msg->header; msg++) { + if (msg->header->nlmsg_type == NLMSG_DONE) return; - if (nh->nlmsg_type != CRYPTO_MSG_GETALG) { + + if (msg->header->nlmsg_type != CRYPTO_MSG_GETALG) { tst_brk(TBROK, "Unexpected message type; type=0x%hx, seq_num=%u", - nh->nlmsg_type, nh->nlmsg_seq); + msg->header->nlmsg_type, + msg->header->nlmsg_seq); } - validate_one_alg(nh); + + validate_one_alg(msg->header); } } @@ -157,35 +151,23 @@ static void run(void) { struct crypto_user_alg payload = { 0 }; struct nlmsghdr nh = { - .nlmsg_len = sizeof(payload), .nlmsg_type = CRYPTO_MSG_GETALG, .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, - .nlmsg_seq = ++(ses.seq_num), - .nlmsg_pid = 0, }; - /* - * Due to an apparent kernel bug, this API cannot be used incrementally, - * so we just use a large recvmsg() buffer. This is good enough since - * we don't necessarily have to check every algorithm for this test to - * be effective... - */ - const size_t bufsize = 1048576; - void *buf = SAFE_MALLOC(bufsize); - size_t res; + struct tst_netlink_message *msg; - SAFE_NETLINK_SEND(ses.fd, &nh, &payload); - - res = SAFE_NETLINK_RECV(ses.fd, buf, bufsize); - - validate_alg_list(buf, res); - - free(buf); + NETLINK_ADD_MESSAGE(ctx, &nh, &payload, sizeof(payload)); + NETLINK_SEND(ctx); + NETLINK_WAIT(ctx); + msg = NETLINK_RECV(ctx); + validate_alg_list(msg); + NETLINK_FREE_MESSAGE(msg); tst_res(TPASS, "No information leaks found"); } static void cleanup(void) { - tst_crypto_close(&ses); + NETLINK_DESTROY_CONTEXT(ctx); } static struct tst_test test = { diff --git a/testcases/kernel/crypto/crypto_user02.c b/testcases/kernel/crypto/crypto_user02.c index afaff5d1..89cbb9bc 100755 --- a/testcases/kernel/crypto/crypto_user02.c +++ b/testcases/kernel/crypto/crypto_user02.c @@ -52,7 +52,7 @@ static const char * const ALGORITHM_CANDIDATES[] = { }; static const char* algorithm = NULL; -static struct tst_crypto_session ses = TST_CRYPTO_SESSION_INIT; +static struct tst_netlink_context *ctx; static void setup(void) @@ -60,7 +60,8 @@ static void setup(void) int rc; unsigned i; struct crypto_user_alg alg; - tst_crypto_open(&ses); + + ctx = NETLINK_CREATE_CONTEXT(NETLINK_CRYPTO); /* find an algorithm, that is not in use */ for (i = 0; i < ARRAY_SIZE(ALGORITHM_CANDIDATES); ++i) { @@ -68,12 +69,12 @@ static void setup(void) strcpy(alg.cru_driver_name, ALGORITHM_CANDIDATES[i]); /* try to add it, to see if it is valid */ - rc = tst_crypto_add_alg(&ses, &alg); + rc = tst_crypto_add_alg(ctx, &alg); if (rc != 0) continue; /* it also has to be deletable */ - rc = tst_crypto_del_alg(&ses, &alg); + rc = tst_crypto_del_alg(ctx, &alg, 1000); if (rc == 0) { algorithm = ALGORITHM_CANDIDATES[i]; break; @@ -103,9 +104,9 @@ static void run(void) if (pid == 0) { /* Child process: execute CRYPTO_MSG_NEWALG. */ - tst_crypto_open(&ses); + ctx = NETLINK_CREATE_CONTEXT(NETLINK_CRYPTO); for (;;) { - TEST(tst_crypto_add_alg(&ses, &alg)); + TEST(tst_crypto_add_alg(ctx, &alg)); if (TST_RET && TST_RET != -EEXIST) tst_brk(TBROK | TRERRNO, "unexpected error from tst_crypto_add_alg()"); @@ -123,7 +124,7 @@ static void run(void) SAFE_WAIT(&status); if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) tst_brk(TBROK, "child %s", tst_strstatus(status)); - TEST(tst_crypto_del_alg(&ses, &alg)); + TEST(tst_crypto_del_alg(ctx, &alg, 1000)); if (TST_RET && TST_RET != -ENOENT) tst_brk(TBROK | TRERRNO, "unexpected error from tst_crypto_del_alg()"); @@ -134,7 +135,7 @@ static void run(void) static void cleanup(void) { - tst_crypto_close(&ses); + NETLINK_DESTROY_CONTEXT(ctx); } static struct tst_test test = { diff --git a/testcases/kernel/crypto/pcrypt_aead01.c b/testcases/kernel/crypto/pcrypt_aead01.c index 3b4f5d8d..3dc2b604 100755 --- a/testcases/kernel/crypto/pcrypt_aead01.c +++ b/testcases/kernel/crypto/pcrypt_aead01.c @@ -26,11 +26,11 @@ #define ATTEMPTS 10000 -static struct tst_crypto_session ses = TST_CRYPTO_SESSION_INIT; +static struct tst_netlink_context *ctx; void setup(void) { - tst_crypto_open(&ses); + ctx = NETLINK_CREATE_CONTEXT(NETLINK_CRYPTO); } void run(void) @@ -43,7 +43,7 @@ void run(void) }; for (i = 0; i < ATTEMPTS; ++i) { - TEST(tst_crypto_add_alg(&ses, &a)); + TEST(tst_crypto_add_alg(ctx, &a)); if (TST_RET && TST_RET == -ENOENT) { tst_brk(TCONF | TRERRNO, "pcrypt, hmac, sha256, cbc or aes not supported"); @@ -51,7 +51,7 @@ void run(void) if (TST_RET && TST_RET != -EEXIST) tst_brk(TBROK | TRERRNO, "add_alg"); - TEST(tst_crypto_del_alg(&ses, &a)); + TEST(tst_crypto_del_alg(ctx, &a, 1000)); if (TST_RET) tst_brk(TBROK | TRERRNO, "del_alg"); @@ -67,7 +67,7 @@ void run(void) void cleanup(void) { - tst_crypto_close(&ses); + NETLINK_DESTROY_CONTEXT(ctx); } static struct tst_test test = { @@ -75,7 +75,7 @@ static struct tst_test test = { .test_all = run, .cleanup = cleanup, .needs_root = 1, - .max_runtime = 300, + .runtime = 300, .tags = (const struct tst_tag[]) { {"linux-git", "d76c68109f37"}, {"CVE", "2017-18075"}, diff --git a/testcases/kernel/device-drivers/acpi/ltp_acpi.c b/testcases/kernel/device-drivers/acpi/ltp_acpi.c index 7dba0455..2c0cc562 100755 --- a/testcases/kernel/device-drivers/acpi/ltp_acpi.c +++ b/testcases/kernel/device-drivers/acpi/ltp_acpi.c @@ -130,11 +130,10 @@ int main(int argc, char *argv[]) int acpi_disabled; tst_parse_opts(argc, argv, NULL, NULL); - tst_require_root(); - tst_sig(FORK, DEF_HANDLER, cleanup); + tst_requires_module_signature_disabled(); tst_module_load(NULL, module_name, NULL); module_loaded = 1; diff --git a/testcases/kernel/device-drivers/acpi/ltp_acpi_cmds.c b/testcases/kernel/device-drivers/acpi/ltp_acpi_cmds.c index d12dd6b9..e34b8b0c 100755 --- a/testcases/kernel/device-drivers/acpi/ltp_acpi_cmds.c +++ b/testcases/kernel/device-drivers/acpi/ltp_acpi_cmds.c @@ -36,9 +36,12 @@ #include #include #include -#include +#ifdef HAVE_LINUX_GENHD_H +# include +#endif #include #include +#include #include "ltp_acpi.h" @@ -123,14 +126,20 @@ static void get_crs_object(acpi_handle handle) static void get_sysfs_path(acpi_handle handle) { - acpi_status status; struct acpi_device *device; kfree(sysfs_path); sysfs_path = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) + acpi_status status; + status = acpi_bus_get_device(handle, &device); if (ACPI_SUCCESS(status)) +#else + device = acpi_fetch_acpi_dev(handle); + if (device) +#endif sysfs_path = kobject_get_path(&device->dev.kobj, GFP_KERNEL); } @@ -257,7 +266,7 @@ static int acpi_init(void) if (acpi_hw_reduced) prk_alert("Detected the Hardware-reduced ACPI mode"); - prk_alert("TEST -- acpi_get_handle "); + prk_alert("TEST -- acpi_get_handle"); status = acpi_get_handle(NULL, "\\_SB", &parent_handle); if (acpi_failure(status, "acpi_get_handle")) return 1; @@ -276,7 +285,7 @@ static int acpi_init(void) dev_info->type); kfree(dev_info); - prk_alert("TEST -- acpi_get_parent "); + prk_alert("TEST -- acpi_get_parent"); status = acpi_get_parent(dev_handle, &parent_handle); return acpi_failure(status, "acpi_get_parent failed"); } @@ -373,7 +382,7 @@ static int acpi_global_lock(void) acpi_status status; u32 global_lock = 0; - prk_alert("TEST -- acpi_acquire_global_lock "); + prk_alert("TEST -- acpi_acquire_global_lock"); if (acpi_hw_reduced) { prk_alert("Skipped due to the HW-reduced mode"); return 0; @@ -382,7 +391,7 @@ static int acpi_global_lock(void) if (acpi_failure(status, "acpi_acquire_global_lock")) return 1; - prk_alert("TEST -- acpi_release_global_lock "); + prk_alert("TEST -- acpi_release_global_lock"); status = acpi_release_global_lock(global_lock); return acpi_failure(status, "acpi_release_global_lock"); } @@ -398,12 +407,18 @@ static int acpi_test_bus(void) if (acpi_failure(status, "acpi_get_handle")) return 1; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) prk_alert("TEST -- acpi_bus_get_device"); status = acpi_bus_get_device(bus_handle, &device); if (acpi_failure(status, "acpi_bus_get_device")) +#else + prk_alert("TEST -- acpi_fetch_acpi_dev"); + device = acpi_fetch_acpi_dev(bus_handle); + if (!device) +#endif return 1; - prk_alert("TEST -- acpi_bus_update_power "); + prk_alert("TEST -- acpi_bus_update_power"); status = acpi_bus_update_power(device->handle, &state); if (acpi_failure(status, "error reading power state")) return 1; @@ -439,7 +454,7 @@ static int acpi_test_resources(void) err |= acpi_failure(status, "get_possible_resources"); #endif - prk_alert("TEST -- acpi_walk_resources "); + prk_alert("TEST -- acpi_walk_resources"); status = acpi_walk_resources(res_handle, METHOD_NAME__CRS, acpi_ec_io_ports, NULL); err |= acpi_failure(status, "Failed walk_resources"); @@ -453,7 +468,7 @@ static int acpi_sleep_test(void) acpi_status status; u32 i; u8 type_a, type_b; - prk_alert("TEST -- acpi_get_sleep_type_data "); + prk_alert("TEST -- acpi_get_sleep_type_data"); for (i = 0; i < ACPI_S_STATE_COUNT; ++i) { status = acpi_get_sleep_type_data(i, &type_a, &type_b); @@ -522,7 +537,7 @@ static acpi_status ltp_get_dev_callback(acpi_handle obj, u32 depth, static int acpi_test_dev_callback(void) { acpi_status status; - prk_alert("TEST -- acpi_get_devices "); + prk_alert("TEST -- acpi_get_devices"); status = acpi_get_devices(NULL, ltp_get_dev_callback, "LTP0001", NULL); return acpi_failure(status, "acpi_get_devices"); } diff --git a/testcases/kernel/device-drivers/block/README b/testcases/kernel/device-drivers/block/README index 812436bb..1490fd29 100755 --- a/testcases/kernel/device-drivers/block/README +++ b/testcases/kernel/device-drivers/block/README @@ -5,19 +5,6 @@ Module under test: linux/block/genhd.c -----------------------------+---------------+--------------- register_blkdev() | linux/fs.h | ltp_block_dev.c unregister_blkdev() | linux/fs.h | ltp_block_dev.c - blk_register_region() | linux/genhd.h | - blk_unregister_region() | linux/genhd.h | - add_disk() | linux/genhd.h | - del_gendisk() | linux/genhd.h | test_genhd.c - blk_lookup_devt() | linux/genhd.h | - alloc_disk() | linux/genhd.h | test_genhd.c - alloc_disk_node() | linux/genhd.h | - get_disk() | linux/genhd.h | - put_disk() | linux/genhd.h | - set_device_ro() | linux/genhd.h | - set_disk_ro() | linux/genhd.h | - bdev_read_only() | linux/fs.h | - invalidate_partition() | linux/fs.h | -For possible test results please see "A POSIX conforming test framework" at -http://www.gnu.org/software/dejagnu/manual/x47.html#posix +For possible test results please see "A POSIX compliant test framework" at +https://www.gnu.org/software/dejagnu/manual/A-POSIX-Conforming-Test-Framework.html diff --git a/testcases/kernel/device-drivers/block/block_dev_kernel/ltp_block_dev.c b/testcases/kernel/device-drivers/block/block_dev_kernel/ltp_block_dev.c index 17047c0d..f50530f2 100755 --- a/testcases/kernel/device-drivers/block/block_dev_kernel/ltp_block_dev.c +++ b/testcases/kernel/device-drivers/block/block_dev_kernel/ltp_block_dev.c @@ -12,8 +12,10 @@ #include #include #include -#include #include +#ifdef HAVE_LINUX_GENHD_H +# include +#endif MODULE_AUTHOR("Márton Németh "); MODULE_AUTHOR("Copyright (c) 2013 Oracle and/or its affiliates"); diff --git a/testcases/kernel/device-drivers/block/block_dev_kernel/test_genhd.c b/testcases/kernel/device-drivers/block/block_dev_kernel/test_genhd.c deleted file mode 100755 index d34a236b..00000000 --- a/testcases/kernel/device-drivers/block/block_dev_kernel/test_genhd.c +++ /dev/null @@ -1,53 +0,0 @@ - -/* - * Module under test: linux/block/genhd.c - * - * Only those functions are tested here which are declared in - * - * Usage: - * 1. make - * 2. su - * 3. insmod ./test_genhd.ko - * 4. Check the test results in "dmesg" - * 5. rmmod test_genhd - */ - -#include -#include - -MODULE_AUTHOR("Márton Németh "); -MODULE_DESCRIPTION("Test block drivers"); -MODULE_LICENSE("GPL"); - -#define BLK_DEV_NAME "test_block" -#define MAX_MAJOR 255 - -static void tc20(void) -{ - struct gendisk *gd_ptr; - - gd_ptr = alloc_disk(1); - if (!gd_ptr) { - return; - } - printk(KERN_DEBUG "gd_ptr after alloc=%p\n", gd_ptr); - - del_gendisk(gd_ptr); -} - -static int test_init_module(void) -{ - printk(KERN_INFO "Starting test_genhd module\n"); - - tc20(); - - return 0; -} - -static void test_exit_module(void) -{ - printk(KERN_DEBUG "Unloading test_genhd module\n"); -} - -module_init(test_init_module); -module_exit(test_exit_module); diff --git a/testcases/kernel/device-drivers/block/block_dev_user/block_dev.c b/testcases/kernel/device-drivers/block/block_dev_user/block_dev.c index 543c3679..cd900d80 100755 --- a/testcases/kernel/device-drivers/block/block_dev_user/block_dev.c +++ b/testcases/kernel/device-drivers/block/block_dev_user/block_dev.c @@ -1,22 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * 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 - * + */ + +/*\ * Test checks block device kernel API. */ @@ -26,74 +15,60 @@ #include #include -#include "test.h" -#include "safe_macros.h" -#include "old_module.h" +#include "tst_test.h" +#include "tst_module.h" -char *TCID = "block_dev"; -int TST_TOTAL = 9; +#define MODULE_NAME "ltp_block_dev" +#define MODULE_NAME_KO MODULE_NAME ".ko" -static const char module_name[] = "ltp_block_dev.ko"; static const char dev_result[] = "/sys/devices/ltp_block_dev/result"; static const char dev_tcase[] = "/sys/devices/ltp_block_dev/tcase"; -static int module_loaded; -static int run_all_testcases; -static const option_t options[] = { - {"a", &run_all_testcases, NULL}, - {NULL, NULL, NULL} +static int module_loaded; +static char *run_all_testcases; +static struct tst_option options[] = { + {"a", &run_all_testcases, "-a\tRun all test-cases (can crash the kernel)"}, + {} }; static void cleanup(void) { if (module_loaded) - tst_module_unload(NULL, module_name); + tst_module_unload(MODULE_NAME_KO); } -static void help(void) +static void run(unsigned int n) { - printf(" -a Run all test-cases (can crash the kernel)\n"); -} + tst_requires_module_signature_disabled(); -void setup(int argc, char *argv[]) -{ - tst_parse_opts(argc, argv, options, help); - - tst_require_root(); - - tst_sig(FORK, DEF_HANDLER, cleanup); -} - -static void test_run(void) -{ - int off = 0; /* * test-cases #8 and #9 can crash the kernel. * We have to wait for kernel fix where register_blkdev() & * unregister_blkdev() checks the input device name parameter * against NULL pointer. */ - if (!run_all_testcases) - off = 2; - - tst_module_load(cleanup, module_name, NULL); - module_loaded = 1; - - int i, pass = 0; - for (i = 0; i < TST_TOTAL - off; ++i) { - SAFE_FILE_PRINTF(cleanup, dev_tcase, "%d", i + 1); - SAFE_FILE_SCANF(cleanup, dev_result, "%d", &pass); - tst_resm((pass) ? TPASS : TFAIL, "Test-case '%d'", i + 1); + n++; + if (!run_all_testcases && (n == 8 || n == 9)) { + tst_res(TCONF, "Skipped n = %d", n); + return; } + + if (!module_loaded) { + tst_module_load(MODULE_NAME_KO, NULL); + module_loaded = 1; + } + + int pass = 0; + + SAFE_FILE_PRINTF(dev_tcase, "%d", n); + SAFE_FILE_SCANF(dev_result, "%d", &pass); + tst_res((pass) ? TPASS : TFAIL, "Test-case '%d'", n); } -int main(int argc, char *argv[]) -{ - setup(argc, argv); - - test_run(); - - cleanup(); - - tst_exit(); -} +static struct tst_test test = { + .needs_root = 1, + .cleanup = cleanup, + .test = run, + .tcnt = 9, + .options = options, +}; diff --git a/testcases/kernel/device-drivers/cpufreq/cpufreq_boost.c b/testcases/kernel/device-drivers/cpufreq/cpufreq_boost.c index 67917b3f..645c4326 100755 --- a/testcases/kernel/device-drivers/cpufreq/cpufreq_boost.c +++ b/testcases/kernel/device-drivers/cpufreq/cpufreq_boost.c @@ -55,10 +55,14 @@ static int id = -1; static int boost_value; -const char governor[] = SYSFS_CPU_DIR "cpu0/cpufreq/scaling_governor"; +static int cpu; + +static const char governor_fmt[] = SYSFS_CPU_DIR "cpu%d/cpufreq/scaling_governor"; +static char governor[64]; static char governor_name[16]; -const char maxspeed[] = SYSFS_CPU_DIR "cpu0/cpufreq/scaling_max_freq"; +static const char maxspeed_fmt[] = SYSFS_CPU_DIR "cpu%d/cpufreq/scaling_max_freq"; +static char maxspeed[64]; static void check_set_turbo(char *file, char *off) { @@ -84,6 +88,38 @@ static void cleanup(void) FILE_PRINTF(governor, "%s", governor_name); } +static int find_boost_cpu(void) +{ + char buf[64]; + int fd, i; + + /* + * The files we're looking for only exist for acpi_cpufreq. Continue + * assuming CPU0 for intel_pstate. + */ + if (!strcmp(cdrv[id].name, "intel_pstate")) + return 0; + + for (i = 0;; i++) { + snprintf(buf, sizeof(buf), SYSFS_CPU_DIR "cpu%d", i); + fd = open(buf, O_RDONLY); + if (fd == -1) + break; + + close(fd); + + snprintf(buf, sizeof(buf), SYSFS_CPU_DIR "cpu%d/cpufreq/boost", i); + fd = open(buf, O_RDONLY); + if (fd == -1) + continue; + + close(fd); + return i; + } + + return -1; +} + static void setup(void) { int fd; @@ -109,6 +145,14 @@ static void setup(void) tst_resm(TINFO, "found '%s' driver, sysfs knob '%s'", cdrv[id].name, cdrv[id].file); + cpu = find_boost_cpu(); + if (cpu == -1) + tst_brkm(TCONF, NULL, "boost not supported by any CPUs"); + + tst_resm(TINFO, "found boost-capable CPU (CPU%d)", cpu); + snprintf(governor, sizeof(governor), governor_fmt, cpu); + snprintf(maxspeed, sizeof(maxspeed), maxspeed_fmt, cpu); + tst_sig(FORK, DEF_HANDLER, cleanup); SAFE_FILE_SCANF(NULL, cdrv[i].file, "%d", &boost_value); @@ -120,16 +164,16 @@ static void setup(void) if (!strcmp(cdrv[i].name, "intel_pstate") && boost_value == cdrv[i].off) check_set_turbo(cdrv[i].file, cdrv[i].off_str); - /* change cpu0 scaling governor */ + /* change cpu scaling governor */ SAFE_FILE_SCANF(NULL, governor, "%s", governor_name); SAFE_FILE_PRINTF(cleanup, governor, "%s", "performance"); - /* use only cpu0 */ + /* use only a single cpu */ cpu_set_t set; CPU_ZERO(&set); - CPU_SET(0, &set); + CPU_SET(cpu, &set); if (sched_setaffinity(0, sizeof(cpu_set_t), &set) < 0) - tst_brkm(TBROK | TERRNO, cleanup, "failed to set CPU0"); + tst_brkm(TBROK | TERRNO, cleanup, "failed to set CPU%d", cpu); struct sched_param params; params.sched_priority = sched_get_priority_max(SCHED_FIFO); @@ -176,12 +220,12 @@ static void test_run(void) /* Enable boost */ if (boost_value == cdrv[id].off) SAFE_FILE_PRINTF(cleanup, cdrv[id].file, "%s", cdrv[id].on_str); - tst_resm(TINFO, "load CPU0 with boost enabled"); + tst_resm(TINFO, "load CPU%d with boost enabled", cpu); boost_time = load_cpu(max_freq_khz); /* Disable boost */ SAFE_FILE_PRINTF(cleanup, cdrv[id].file, "%s", cdrv[id].off_str); - tst_resm(TINFO, "load CPU0 with boost disabled"); + tst_resm(TINFO, "load CPU%d with boost disabled", cpu); boost_off_time = load_cpu(max_freq_khz); boost_off_time *= .98; diff --git a/testcases/kernel/device-drivers/include/includeTest.c b/testcases/kernel/device-drivers/include/includeTest.c index d5b6fe22..a13eb292 100755 --- a/testcases/kernel/device-drivers/include/includeTest.c +++ b/testcases/kernel/device-drivers/include/includeTest.c @@ -33,7 +33,9 @@ #include #include #include -#include +#ifdef HAVE_LINUX_GENHD_H +# include +#endif #include #include #include diff --git a/testcases/kernel/device-drivers/nls/nlsTest.c b/testcases/kernel/device-drivers/nls/nlsTest.c index f0c39ab9..e50ffa71 100755 --- a/testcases/kernel/device-drivers/nls/nlsTest.c +++ b/testcases/kernel/device-drivers/nls/nlsTest.c @@ -31,7 +31,9 @@ #include #include #include -#include +#ifdef HAVE_LINUX_GENHD_H +# include +#endif #include #include #include diff --git a/testcases/kernel/device-drivers/pci/tpci_kernel/.gitignore b/testcases/kernel/device-drivers/pci/tpci_kernel/.gitignore index 3e100ad5..ed4712e3 100755 --- a/testcases/kernel/device-drivers/pci/tpci_kernel/.gitignore +++ b/testcases/kernel/device-drivers/pci/tpci_kernel/.gitignore @@ -5,3 +5,4 @@ /.*.ko /.*.cmd /Module.symvers +modules.livepatch diff --git a/testcases/kernel/device-drivers/pci/tpci_user/tpci.c b/testcases/kernel/device-drivers/pci/tpci_user/tpci.c index 96018f18..5d241fb4 100755 --- a/testcases/kernel/device-drivers/pci/tpci_user/tpci.c +++ b/testcases/kernel/device-drivers/pci/tpci_user/tpci.c @@ -50,8 +50,8 @@ static void cleanup(void) void setup(void) { tst_require_root(); - tst_sig(FORK, DEF_HANDLER, cleanup); + tst_requires_module_signature_disabled(); } static void run_pci_testcases(int bus, int slot) diff --git a/testcases/kernel/device-drivers/rtc/rtc02.c b/testcases/kernel/device-drivers/rtc/rtc02.c index dbac11b8..34509abc 100755 --- a/testcases/kernel/device-drivers/rtc/rtc02.c +++ b/testcases/kernel/device-drivers/rtc/rtc02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * RTC device set time function test. * * [Algorithm] diff --git a/testcases/kernel/device-drivers/tbio/tbio_kernel/ltp_tbio.c b/testcases/kernel/device-drivers/tbio/tbio_kernel/ltp_tbio.c index 0a9cd40e..94435e4c 100755 --- a/testcases/kernel/device-drivers/tbio/tbio_kernel/ltp_tbio.c +++ b/testcases/kernel/device-drivers/tbio/tbio_kernel/ltp_tbio.c @@ -43,7 +43,9 @@ #include #include #include -#include +#ifdef HAVE_LINUX_GENHD_H +# include +#endif #include #include diff --git a/testcases/kernel/device-drivers/uaccess/.gitignore b/testcases/kernel/device-drivers/uaccess/.gitignore index cf59b68d..c42a3fbe 100755 --- a/testcases/kernel/device-drivers/uaccess/.gitignore +++ b/testcases/kernel/device-drivers/uaccess/.gitignore @@ -6,3 +6,4 @@ /.*.ko /.*.cmd /Module.symvers +modules.livepatch diff --git a/testcases/kernel/device-drivers/uaccess/uaccess.c b/testcases/kernel/device-drivers/uaccess/uaccess.c index f682ff7f..ab6fa58a 100755 --- a/testcases/kernel/device-drivers/uaccess/uaccess.c +++ b/testcases/kernel/device-drivers/uaccess/uaccess.c @@ -95,9 +95,9 @@ int main(int argc, char *argv[]) tst_parse_opts(argc, argv, NULL, NULL); tst_require_root(); - tst_sig(FORK, DEF_HANDLER, cleanup); + tst_requires_module_signature_disabled(); tst_module_load(NULL, module_name, NULL); module_loaded = 1; diff --git a/testcases/kernel/device-drivers/zram/zram01.sh b/testcases/kernel/device-drivers/zram/zram01.sh index 6bc305f2..793f6603 100755 --- a/testcases/kernel/device-drivers/zram/zram01.sh +++ b/testcases/kernel/device-drivers/zram/zram01.sh @@ -82,7 +82,8 @@ zram_makefs() mkfs.$fs /dev/zram$i > err.log 2>&1 if [ $? -ne 0 ]; then cat err.log - tst_brk TFAIL "failed to make $fs on /dev/zram$i" + tst_res TFAIL "Failed to make $fs on /dev/zram$i" + tst_brk TBROK "Can't continue with mounting the FS" fi i=$(($i + 1)) @@ -153,7 +154,7 @@ zram_fill_fs() continue fi - TST_RETRY_FUNC "check_read_mem_used_total /sys/block/zram$i/mm_stat" 0 + TST_RETRY_FN_EXP_BACKOFF "check_read_mem_used_total /sys/block/zram$i/mm_stat" 0 10 mem_used_total=$(read_mem_used_total /sys/block/zram$i/mm_stat) tst_res TINFO "mem_used_total: $mem_used_total" diff --git a/testcases/kernel/device-drivers/zram/zram03.c b/testcases/kernel/device-drivers/zram/zram03.c index 98eb61e1..8cf26de4 100755 --- a/testcases/kernel/device-drivers/zram/zram03.c +++ b/testcases/kernel/device-drivers/zram/zram03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * zram: generic RAM based compressed R/W block devices * http://lkml.org/lkml/2010/8/9/227 * @@ -89,7 +87,7 @@ static void verify_device(void) SAFE_CLOSE(fd); } -static void reset(void) +static void reset_zram(void) { char reset_path[200]; @@ -163,7 +161,7 @@ static void run(void) dump_info(); verify_device(); - reset(); + reset_zram(); dump_info(); } @@ -181,7 +179,7 @@ static void setup(void) tst_res(TINFO, "zram module already loaded, kernel supports zram-control interface"); SAFE_FILE_SCANF(HOT_ADD_PATH, "%d", &dev_num); - hot_add_flag =1; + hot_add_flag = 1; goto fill_path; } diff --git a/testcases/kernel/device-drivers/zram/zram_lib.sh b/testcases/kernel/device-drivers/zram/zram_lib.sh index e94d7db1..e94f9244 100755 --- a/testcases/kernel/device-drivers/zram/zram_lib.sh +++ b/testcases/kernel/device-drivers/zram/zram_lib.sh @@ -108,12 +108,16 @@ zram_max_streams() for max_s in $zram_max_streams; do local sys_path="/sys/block/zram${i}/max_comp_streams" - echo $max_s > $sys_path || \ - tst_brk TFAIL "failed to set '$max_s' to $sys_path" + if ! echo $max_s > $sys_path; then + tst_res TFAIL "failed to set '$max_s' to $sys_path" + return + fi local max_streams=$(cat $sys_path) - [ "$max_s" -ne "$max_streams" ] && \ - tst_brk TFAIL "can't set max_streams '$max_s', get $max_stream" + if [ "$max_s" -ne "$max_streams" ]; then + tst_res TFAIL "can't set max_streams '$max_s', get $max_stream" + return + fi i=$(($i + 1)) tst_res TINFO "$sys_path = '$max_streams'" @@ -140,8 +144,10 @@ zram_compress_alg() for i in $(seq $dev_start $dev_end); do for alg in $algs; do local sys_path="/sys/block/zram${i}/comp_algorithm" - echo "$alg" > $sys_path || \ - tst_brk TFAIL "can't set '$alg' to $sys_path" + if ! echo "$alg" > $sys_path; then + tst_res TFAIL "can't set '$alg' to $sys_path" + return + fi tst_res TINFO "$sys_path = '$alg'" done done @@ -157,8 +163,10 @@ zram_set_disksizes() tst_res TINFO "set disk size to zram device(s)" for ds in $zram_sizes; do local sys_path="/sys/block/zram${i}/disksize" - echo "$ds" > $sys_path || \ - tst_brk TFAIL "can't set '$ds' to $sys_path" + if ! echo "$ds" > $sys_path; then + tst_res TFAIL "can't set '$ds' to $sys_path" + return + fi i=$(($i + 1)) tst_res TINFO "$sys_path = '$ds'" @@ -183,8 +191,10 @@ zram_set_memlimit() for ds in $zram_mem_limits; do local sys_path="/sys/block/zram${i}/mem_limit" - echo "$ds" > $sys_path || \ - tst_brk TFAIL "can't set '$ds' to $sys_path" + if ! echo "$ds" > $sys_path; then + tst_res TFAIL "can't set '$ds' to $sys_path" + return + fi i=$(($i + 1)) tst_res TINFO "$sys_path = '$ds'" diff --git a/testcases/kernel/firmware/fw_load_kernel/.gitignore b/testcases/kernel/firmware/fw_load_kernel/.gitignore index 180072a7..6fc82952 100755 --- a/testcases/kernel/firmware/fw_load_kernel/.gitignore +++ b/testcases/kernel/firmware/fw_load_kernel/.gitignore @@ -4,3 +4,4 @@ /Module.symvers /ltp_fw_load.mod.c /.tmp_versions/ +modules.livepatch diff --git a/testcases/kernel/firmware/fw_load_user/fw_load.c b/testcases/kernel/firmware/fw_load_user/fw_load.c index 83648b62..b2ed09e6 100755 --- a/testcases/kernel/firmware/fw_load_user/fw_load.c +++ b/testcases/kernel/firmware/fw_load_user/fw_load.c @@ -102,7 +102,6 @@ static void help(void) void setup(int argc, char *argv[]) { - tst_parse_opts(argc, argv, options, help); if (nflag) { @@ -113,6 +112,7 @@ void setup(int argc, char *argv[]) } tst_require_root(); + tst_requires_module_signature_disabled(); char fw_size_param[19]; snprintf(fw_size_param, 19, "fw_size=%d", fw_size); diff --git a/testcases/kernel/fs/doio/growfiles.c b/testcases/kernel/fs/doio/growfiles.c index eb58ce0c..7bf51fb9 100755 --- a/testcases/kernel/fs/doio/growfiles.c +++ b/testcases/kernel/fs/doio/growfiles.c @@ -328,7 +328,7 @@ int Open_flags[] = { #define USECS_PER_SEC 1000000 /* microseconds per second */ /* - * Define marcos used when dealing with file locks. + * Define macros used when dealing with file locks. */ #define LKLVL0 1 /* file lock around write/read/trunc */ #define LKLVL1 2 /* file lock after open to before close */ diff --git a/testcases/kernel/fs/doio/rwtest b/testcases/kernel/fs/doio/rwtest index 6725e142..3fbdf7f9 100755 --- a/testcases/kernel/fs/doio/rwtest +++ b/testcases/kernel/fs/doio/rwtest @@ -327,13 +327,6 @@ do then blks=${szblks[$n]} else - # If df is a symlink (to busybox) then do not pass the $dir and $dfOpts - # parameters because they don't work as expected - if test -h $(which df) - then - dir=""; dfOpts=""; - fi - blks=$(df $dfOpts $dir | (while read fs blks used avail cap mountpoint do diff --git a/testcases/kernel/fs/fs_bind/fs_bind_lib.sh b/testcases/kernel/fs/fs_bind/fs_bind_lib.sh index 52190a7c..72337473 100755 --- a/testcases/kernel/fs/fs_bind/fs_bind_lib.sh +++ b/testcases/kernel/fs/fs_bind/fs_bind_lib.sh @@ -7,7 +7,6 @@ TST_NEEDS_TMPDIR=1 TST_NEEDS_ROOT=1 -TST_MIN_KVER=2.6.15 TST_SETUP="${TST_SETUP:-fs_bind_setup}" TST_CLEANUP="${TST_CLEANUP:-fs_bind_cleanup}" TST_TESTFUNC=fs_bind_test diff --git a/testcases/kernel/fs/fs_fill/fs_fill.c b/testcases/kernel/fs/fs_fill/fs_fill.c index 2ecd8e2a..131128db 100755 --- a/testcases/kernel/fs/fs_fill/fs_fill.c +++ b/testcases/kernel/fs/fs_fill/fs_fill.c @@ -22,7 +22,7 @@ static volatile int run; static unsigned int nthreads; -static int enospc_cnt; +static tst_atomic_t enospc_cnt; static struct worker *workers; struct worker { @@ -121,8 +121,9 @@ static void cleanup(void) } static struct tst_test test = { - .max_runtime = 60, + .timeout = 300, .needs_root = 1, + .dev_min_size = 1024, .mount_device = 1, .mntpoint = MNTPOINT, .all_filesystems = 1, diff --git a/testcases/kernel/fs/fsplough/.gitignore b/testcases/kernel/fs/fsplough/.gitignore new file mode 100644 index 00000000..34fee75c --- /dev/null +++ b/testcases/kernel/fs/fsplough/.gitignore @@ -0,0 +1 @@ +/fsplough diff --git a/testcases/kernel/syscalls/getdtablesize/Makefile b/testcases/kernel/fs/fsplough/Makefile old mode 100755 new mode 100644 similarity index 74% rename from testcases/kernel/syscalls/getdtablesize/Makefile rename to testcases/kernel/fs/fsplough/Makefile index 044619fb..6504e9f8 --- a/testcases/kernel/syscalls/getdtablesize/Makefile +++ b/testcases/kernel/fs/fsplough/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) International Business Machines Corp., 2001 +# Copyright (c) 2024 Linux Test Project top_srcdir ?= ../../../.. diff --git a/testcases/kernel/fs/fsplough/fsplough.c b/testcases/kernel/fs/fsplough/fsplough.c new file mode 100644 index 00000000..65db3738 --- /dev/null +++ b/testcases/kernel/fs/fsplough/fsplough.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC + */ + +/*\ + * Write data into a test file using various methods and verify that file + * contents match what was written. + */ + +#define _GNU_SOURCE +#include +#include +#include "tst_test.h" +#include "tst_safe_prw.h" + +#define MAX_VEC 8 +#define TEST_FILENAME "fsplough.dat" + +typedef void (*io_func)(void *buf, size_t offset, size_t size); + +static char *workdir_arg; +static char *directwr_flag; +static char *directrd_flag; +static char *loop_arg; +static int loop_count; + +static int read_fd = -1, write_fd = -1; +static char *writebuf, *filedata; +static size_t blocksize, bufsize, filesize; + +static struct tst_test test; +static void do_write(void *buf, size_t offset, size_t size); +static void do_pwrite(void *buf, size_t offset, size_t size); +static void do_writev(void *buf, size_t offset, size_t size); +static void do_pwritev(void *buf, size_t offset, size_t size); +static void do_read(void *buf, size_t offset, size_t size); +static void do_pread(void *buf, size_t offset, size_t size); +static void do_readv(void *buf, size_t offset, size_t size); +static void do_preadv(void *buf, size_t offset, size_t size); + +static const io_func write_funcs[] = { + do_write, + do_pwrite, + do_writev, + do_pwritev +}; + +static const io_func read_funcs[] = { + do_read, + do_pread, + do_readv, + do_preadv +}; + +static size_t fill_buffer(char *buf, size_t size) +{ + size_t i, ret = MAX_VEC + 1 + rand() % (size - MAX_VEC); + + /* Align buffer size to block size */ + if (directwr_flag || directrd_flag) + ret = MAX(LTP_ALIGN(ret, blocksize), MAX_VEC * blocksize); + + for (i = 0; i < ret; i++) + buf[i] = rand(); + + return ret; +} + +static void vectorize_buffer(struct iovec *vec, size_t vec_size, char *buf, + size_t buf_size, int align) +{ + size_t i, len, chunk = align ? blocksize : 1; + + memset(vec, 0, vec_size * sizeof(struct iovec)); + buf_size /= chunk; + + for (i = 0; buf_size && i < vec_size; i++) { + len = 1 + rand() % (buf_size + i + 1 - vec_size); + vec[i].iov_base = buf; + vec[i].iov_len = len * chunk; + buf += vec[i].iov_len; + buf_size -= len; + } + + vec[vec_size - 1].iov_len += buf_size * chunk; +} + +static void update_filedata(const void *buf, size_t offset, size_t size) +{ + memcpy(filedata + offset, buf, size * sizeof(char)); +} + +static void do_write(void *buf, size_t offset, size_t size) +{ + SAFE_LSEEK(write_fd, offset, SEEK_SET); + SAFE_WRITE(1, write_fd, buf, size); +} + +static void do_pwrite(void *buf, size_t offset, size_t size) +{ + SAFE_PWRITE(1, write_fd, buf, size, offset); +} + +static void do_writev(void *buf, size_t offset, size_t size) +{ + struct iovec vec[MAX_VEC] = {}; + + vectorize_buffer(vec, MAX_VEC, buf, size, !!directwr_flag); + SAFE_LSEEK(write_fd, offset, SEEK_SET); + SAFE_WRITEV(1, write_fd, vec, MAX_VEC); +} + +static void do_pwritev(void *buf, size_t offset, size_t size) +{ + struct iovec vec[MAX_VEC] = {}; + + vectorize_buffer(vec, MAX_VEC, buf, size, !!directwr_flag); + SAFE_PWRITEV(1, write_fd, vec, MAX_VEC, offset); +} + +static void do_read(void *buf, size_t offset, size_t size) +{ + SAFE_LSEEK(read_fd, offset, SEEK_SET); + SAFE_READ(1, read_fd, buf, size); +} + +static void do_pread(void *buf, size_t offset, size_t size) +{ + SAFE_PREAD(1, read_fd, buf, size, offset); +} + +static void do_readv(void *buf, size_t offset, size_t size) +{ + struct iovec vec[MAX_VEC] = {}; + + vectorize_buffer(vec, MAX_VEC, buf, size, !!directrd_flag); + SAFE_LSEEK(read_fd, offset, SEEK_SET); + SAFE_READV(1, read_fd, vec, MAX_VEC); +} + +static void do_preadv(void *buf, size_t offset, size_t size) +{ + struct iovec vec[MAX_VEC] = {}; + + vectorize_buffer(vec, MAX_VEC, buf, size, !!directrd_flag); + SAFE_PREADV(1, read_fd, vec, MAX_VEC, offset); +} + +static int open_testfile(int flags) +{ + if ((flags & O_WRONLY) && directwr_flag) + flags |= O_DIRECT; + + if ((flags & O_RDONLY) && directrd_flag) + flags |= O_DIRECT; + + return SAFE_OPEN(TEST_FILENAME, flags, 0644); +} + +static void setup(void) +{ + struct statvfs statbuf; + size_t pagesize; + int runtime; + + srand(time(0)); + pagesize = SAFE_SYSCONF(_SC_PAGESIZE); + + if (workdir_arg) + SAFE_CHDIR(workdir_arg); + + if (tst_parse_int(loop_arg, &loop_count, 0, INT_MAX)) + tst_brk(TBROK, "Invalid write loop count: %s", loop_arg); + + write_fd = open_testfile(O_WRONLY | O_CREAT | O_TRUNC); + read_fd = open_testfile(O_RDONLY); + TEST(fstatvfs(write_fd, &statbuf)); + + if (TST_RET == -1) + tst_brk(TBROK | TTERRNO, "fstatvfs() failed"); + else if (TST_RET) + tst_brk(TBROK | TTERRNO, "Invalid fstatvfs() return value"); + + blocksize = statbuf.f_bsize; + tst_res(TINFO, "Block size: %zu", blocksize); + bufsize = 4 * MAX_VEC * MAX(pagesize, blocksize); + filesize = 1024 * MAX(pagesize, blocksize); + writebuf = SAFE_MMAP(NULL, bufsize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + filedata = SAFE_MALLOC(filesize); + + if (loop_arg) { + /* + * Executing fixed number of loops. Use calculated runtime + * as timeout and apply the timeout multiplier. + */ + runtime = bufsize * loop_count / (8 * 1024 * 1024); + runtime = tst_multiply_timeout(runtime); + + if (runtime > test.runtime) + tst_set_runtime(runtime); + } +} + +static void run(void) +{ + size_t start, length; + int i, f, fails = 0; + + /* Test data consistency between random writes */ + for (i = 0; !loop_arg || i < loop_count; i++) { + if (!tst_remaining_runtime()) + break; + + length = fill_buffer(writebuf, bufsize); + start = rand() % (filesize + 1 - length); + + /* Align offset to blocksize if needed */ + if (directrd_flag || directwr_flag) + start = (start + blocksize / 2) & ~(blocksize - 1); + + update_filedata(writebuf, start, length); + f = rand() % ARRAY_SIZE(write_funcs); + write_funcs[f](writebuf, start, length); + + memset(writebuf, 0, length); + f = rand() % ARRAY_SIZE(read_funcs); + read_funcs[f](writebuf, start, length); + + if (memcmp(writebuf, filedata + start, length)) { + tst_res(TFAIL, "Partial data mismatch at [%zu:%zu]", + start, start + length); + fails++; + } + } + + if (i < loop_count / 2) { + tst_res(TWARN, "Runtime expired, exiting early after %d loops", + i); + tst_res(TINFO, "If you are running on slow machine, " + "try exporting LTP_TIMEOUT_MUL > 1"); + } else if (i < loop_count) { + tst_res(TINFO, "Runtime expired, exiting early after %d loops", + i); + } else if (!loop_arg && i < 10) { + tst_res(TWARN, "Slow system: test performed only %d loops!", i); + } else { + tst_res(TPASS, "Exiting after %d loops", i); + } + + if (!fails) + tst_res(TPASS, "Partial data are consistent"); + + /* Ensure that the testfile has the expected size */ + do_write(writebuf, filesize - blocksize, blocksize); + update_filedata(writebuf, filesize - blocksize, blocksize); + + /* Sync the testfile and clear cache */ + SAFE_CLOSE(read_fd); + SAFE_FSYNC(write_fd); + SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "1"); + read_fd = open_testfile(O_RDONLY); + + /* Check final file contents */ + for (start = 0; start < filesize; start += bufsize) { + length = MIN(bufsize, filesize - start); + SAFE_READ(1, read_fd, writebuf, length); + + if (memcmp(writebuf, filedata + start, length)) { + tst_res(TFAIL, "Final data mismatch at [%zu:%zu]", + start, start + length); + return; + } + } + + tst_res(TPASS, "Final data are consistent"); +} + +static void cleanup(void) +{ + SAFE_MUNMAP(writebuf, bufsize); + free(filedata); + + if (read_fd >= 0) + SAFE_CLOSE(read_fd); + + if (write_fd >= 0) + SAFE_CLOSE(write_fd); + + SAFE_UNLINK(TEST_FILENAME); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .runtime = 30, + .options = (struct tst_option[]) { + {"c:", &loop_arg, + "Number of write loops (default: loop for 30 seconds)"}, + {"d:", &workdir_arg, "Path to working directory"}, + {"W", &directwr_flag, "Use direct I/O for writing"}, + {"R", &directrd_flag, "Use direct I/O for reading"}, + {} + } +}; diff --git a/testcases/kernel/fs/fsx-linux/Makefile b/testcases/kernel/fs/fsx-linux/Makefile index 956486b8..5d04d81b 100755 --- a/testcases/kernel/fs/fsx-linux/Makefile +++ b/testcases/kernel/fs/fsx-linux/Makefile @@ -22,13 +22,11 @@ top_srcdir ?= ../../../.. -include $(top_srcdir)/include/mk/env_pre.mk +include $(top_srcdir)/include/mk/testcases.mk CPPFLAGS += -DNO_XFS -I$(abs_srcdir) \ -D_LARGEFILE64_SOURCE -D_GNU_SOURCE WCFLAGS += -w -INSTALL_TARGETS := fsxtest* - include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/fs/fsx-linux/fsx-linux.c b/testcases/kernel/fs/fsx-linux/fsx-linux.c index 64c27a0f..2e0f17a5 100755 --- a/testcases/kernel/fs/fsx-linux/fsx-linux.c +++ b/testcases/kernel/fs/fsx-linux/fsx-linux.c @@ -1,1353 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 1991, NeXT Computer, Inc. All Rights Reserverd. - * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - * - * File: fsx.c * Author: Avadis Tevanian, Jr. * - * File system exerciser. + * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. + * Conrad Minshall + * Dave Jones + * Zach Brown + * Joe Sokol, Pat Dirks, Clark Warner, Guy Harris * - * Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com - * - * Various features from Joe Sokol, Pat Dirks, and Clark Warner. - * - * Small changes to work under Linux -- davej@suse.de - * - * Sundry porting patches from Guy Harris 12/2001 - * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.1 2001/12/20 04:15:57 jkh Exp $ - * - * Add multi-file testing feature -- Zach Brown + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ + +/*\ + * This is a complete rewrite of the old fsx-linux tool, created by + * NeXT Computer, Inc. and Apple Computer, Inc. between 1991 and 2001, + * then adapted for LTP. Test is actually a file system exerciser: we bring a + * file and randomly write operations like read/write/map read/map write and + * truncate, according with input parameters. Then we check if all of them + * have been completed. */ -#include -#include -#if defined(_UWIN) || defined(__linux__) -#include -#include -#include -#include -#include -#endif -#include -#include -#ifndef MAP_FILE -#define MAP_FILE 0 -#endif -#include -#include -#include #include -#include -#include -#include -#include +#include "tst_test.h" -/* - * A log entry is an operation and a bunch of arguments. - */ +#define FNAME "ltp-file.bin" -struct log_entry { - int operation; - struct timeval tv; - int args[3]; +enum { + OP_READ = 0, + OP_WRITE, + OP_TRUNCATE, + OP_MAPREAD, + OP_MAPWRITE, + /* keep counter here */ + OP_TOTAL, }; -#define LOGSIZE 1000 +static char *str_file_max_size; +static char *str_op_max_size; +static char *str_op_nums; +static char *str_op_write_align; +static char *str_op_read_align; +static char *str_op_trunc_align; -struct log_entry oplog[LOGSIZE]; /* the log */ -int logptr = 0; /* current position in log */ -int logcount = 0; /* total ops */ +static int file_desc; +static long long file_max_size = 256 * 1024; +static long long op_max_size = 64 * 1024; +static long long file_size; +static int op_write_align = 1; +static int op_read_align = 1; +static int op_trunc_align = 1; +static int op_nums = 1000; +static int page_size; -/* - * Define operations - */ +static char *file_buff; +static char *temp_buff; -#define OP_READ 1 -#define OP_WRITE 2 -#define OP_TRUNCATE 3 -#define OP_CLOSEOPEN 4 -#define OP_MAPREAD 5 -#define OP_MAPWRITE 6 -#define OP_SKIPPED 7 +struct file_pos_t { + long long offset; + long long size; +}; -int page_size; -int page_mask; - -char *original_buf; /* a pointer to the original data */ -char *good_buf; /* a pointer to the correct data */ -char *temp_buf; /* a pointer to the current data */ -char *fname; /* name of our test file */ -char logfile[1024]; /* name of our log file */ -char goodfile[1024]; /* name of our test file */ - -off_t file_size = 0; -off_t biggest = 0; -char state[256]; -unsigned long testcalls = 0; /* calls to function "test" */ - -unsigned long simulatedopcount = 0; /* -b flag */ -int closeprob = 0; /* -c flag */ -int debug = 0; /* -d flag */ -unsigned long debugstart = 0; /* -D flag */ -unsigned long maxfilelen = 256 * 1024; /* -l flag */ -int sizechecks = 1; /* -n flag disables them */ -int maxoplen = 64 * 1024; /* -o flag */ -int quiet = 0; /* -q flag */ -unsigned long progressinterval = 0; /* -p flag */ -int readbdy = 1; /* -r flag */ -int style = 0; /* -s flag */ -int truncbdy = 1; /* -t flag */ -int writebdy = 1; /* -w flag */ -long monitorstart = -1; /* -m flag */ -long monitorend = -1; /* -m flag */ -int lite = 0; /* -L flag */ -long numops = -1; /* -N flag */ -int randomoplen = 1; /* -O flag disables it */ -int seed = 1; /* -S flag */ -int mapped_writes = 1; /* -W flag disables */ -int mapped_reads = 1; /* -R flag disables it */ -int fsxgoodfd = 0; -FILE *fsxlogf = NULL; -int badoff = -1; - -void vwarnc(int code,const char *fmt, va_list ap) +static void op_align_pages(struct file_pos_t *pos) { - fprintf(stderr, "fsx: "); - if (fmt != NULL) { - vfprintf(stderr, fmt, ap); - fprintf(stderr, ": "); + long long pg_offset; + + pg_offset = pos->offset % page_size; + + pos->offset -= pg_offset; + pos->size += pg_offset; +} + +static void op_file_position( + const long long fsize, + const int align, + struct file_pos_t *pos) +{ + long long diff; + + pos->offset = random() % fsize; + pos->size = random() % (fsize - pos->offset); + + diff = pos->offset % align; + + if (diff) { + pos->offset -= diff; + pos->size += diff; } - fprintf(stderr, "%s\n", strerror(code)); + + if (!pos->size) + pos->size = 1; } -void warn(const char *fmt, ...) +static void update_file_size(struct file_pos_t const *pos) { - va_list ap; - va_start(ap, fmt); - vwarnc(errno, fmt, ap); - va_end(ap); -} - -void - __attribute__ ((format(printf, 1, 2))) - prt(char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vfprintf(stdout, fmt, args); - va_end(args); - - if (fsxlogf) { - va_start(args, fmt); - vfprintf(fsxlogf, fmt, args); - va_end(args); + if (pos->offset + pos->size > file_size) { + file_size = pos->offset + pos->size; + tst_res(TDEBUG, "File size changed: %llu", file_size); } } -void prterr(char *prefix) +static int memory_compare( + const char *a, + const char *b, + const long long offset, + const long long size) { - prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); -} + int diff; -void log4(int operation, int arg0, int arg1, int arg2, struct timeval *tv) -{ - struct log_entry *le; - - le = &oplog[logptr]; - le->tv = *tv; - le->operation = operation; - le->args[0] = arg0; - le->args[1] = arg1; - le->args[2] = arg2; - logptr++; - logcount++; - if (logptr >= LOGSIZE) - logptr = 0; -} - -void logdump(void) -{ - int i, count, down; - struct log_entry *lp; - - prt("LOG DUMP (%d total operations):\n", logcount); - if (logcount < LOGSIZE) { - i = 0; - count = logcount; - } else { - i = logptr; - count = LOGSIZE; + for (long long i = 0; i < size; i++) { + diff = a[i] - b[i]; + if (diff) { + tst_res(TDEBUG, "File memory differs at offset=%llu ('%c' != '%c')", + offset + i, a[i], b[i]); + break; + } } - for (; count > 0; count--) { - int opnum; - opnum = i + 1 + (logcount / LOGSIZE) * LOGSIZE; - lp = &oplog[i]; - prt("%d: %lu.%06lu ", opnum, lp->tv.tv_sec, lp->tv.tv_usec); + return diff; +} - switch (lp->operation) { +static int op_read(void) +{ + if (!file_size) { + tst_res(TINFO, "Skipping zero size read"); + return 0; + } + + struct file_pos_t pos; + + op_file_position(file_size, op_read_align, &pos); + + tst_res(TDEBUG, "Reading at offset=%llu, size=%llu", + pos.offset, pos.size); + + memset(temp_buff, 0, file_max_size); + + SAFE_LSEEK(file_desc, (off_t)pos.offset, SEEK_SET); + SAFE_READ(0, file_desc, temp_buff, pos.size); + + int ret = memory_compare( + file_buff + pos.offset, + temp_buff, + pos.offset, + pos.size); + + if (ret) + return -1; + + return 1; +} + +static int op_write(void) +{ + if (file_size >= file_max_size) { + tst_res(TINFO, "Skipping max size write"); + return 0; + } + + struct file_pos_t pos; + char data; + + op_file_position(file_max_size, op_write_align, &pos); + + for (long long i = 0; i < pos.size; i++) { + data = random() % 10 + 'a'; + + file_buff[pos.offset + i] = data; + temp_buff[i] = data; + } + + tst_res(TDEBUG, "Writing at offset=%llu, size=%llu", + pos.offset, pos.size); + + SAFE_LSEEK(file_desc, (off_t)pos.offset, SEEK_SET); + SAFE_WRITE(SAFE_WRITE_ALL, file_desc, temp_buff, pos.size); + + update_file_size(&pos); + + return 1; +} + +static int op_truncate(void) +{ + struct file_pos_t pos; + + op_file_position(file_max_size, op_trunc_align, &pos); + file_size = pos.offset + pos.size; + + tst_res(TDEBUG, "Truncating to %llu", file_size); + + SAFE_FTRUNCATE(file_desc, file_size); + memset(file_buff + file_size, 0, file_max_size - file_size); + + return 1; +} + +static int op_map_read(void) +{ + if (!file_size) { + tst_res(TINFO, "Skipping zero size read"); + return 0; + } + + struct file_pos_t pos; + char *addr; + + op_file_position(file_size, op_read_align, &pos); + op_align_pages(&pos); + + tst_res(TDEBUG, "Map reading at offset=%llu, size=%llu", + pos.offset, pos.size); + + addr = SAFE_MMAP( + 0, pos.size, + PROT_READ, + MAP_FILE | MAP_SHARED, + file_desc, + (off_t)pos.offset); + + memcpy(file_buff + pos.offset, addr, pos.size); + + int ret = memory_compare( + addr, + file_buff + pos.offset, + pos.offset, + pos.size); + + SAFE_MUNMAP(addr, pos.size); + if (ret) + return -1; + + return 1; +} + +static int op_map_write(void) +{ + if (file_size >= file_max_size) { + tst_res(TINFO, "Skipping max size write"); + return 0; + } + + struct file_pos_t pos; + char *addr; + + op_file_position(file_max_size, op_write_align, &pos); + op_align_pages(&pos); + + if (file_size < pos.offset + pos.size) + SAFE_FTRUNCATE(file_desc, pos.offset + pos.size); + + tst_res(TDEBUG, "Map writing at offset=%llu, size=%llu", + pos.offset, pos.size); + + for (long long i = 0; i < pos.size; i++) + file_buff[pos.offset + i] = random() % 10 + 'l'; + + addr = SAFE_MMAP( + 0, pos.size, + PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, + file_desc, + (off_t)pos.offset); + + memcpy(addr, file_buff + pos.offset, pos.size); + SAFE_MSYNC(addr, pos.size, MS_SYNC); + SAFE_MUNMAP(addr, pos.size); + update_file_size(&pos); + + return 1; +} + +static void run(void) +{ + int op; + int ret; + int counter = 0; + + file_size = 0; + + memset(file_buff, 0, file_max_size); + memset(temp_buff, 0, file_max_size); + + SAFE_FTRUNCATE(file_desc, 0); + + while (counter < op_nums) { + op = random() % OP_TOTAL; + + switch (op) { + case OP_WRITE: + ret = op_write(); + break; case OP_MAPREAD: - prt("MAPREAD 0x%x thru 0x%x (0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && badoff < - lp->args[0] + lp->args[1]) - prt("\t***RRRR***"); + ret = op_map_read(); break; case OP_MAPWRITE: - prt("MAPWRITE 0x%x thru 0x%x (0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && badoff < - lp->args[0] + lp->args[1]) - prt("\t******WWWW"); - break; - case OP_READ: - prt("READ 0x%x thru 0x%x (0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && - badoff < lp->args[0] + lp->args[1]) - prt("\t***RRRR***"); - break; - case OP_WRITE: - prt("WRITE 0x%x thru 0x%x (0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (lp->args[0] > lp->args[2]) - prt(" HOLE"); - else if (lp->args[0] + lp->args[1] > lp->args[2]) - prt(" EXTEND"); - if ((badoff >= lp->args[0] || badoff >= lp->args[2]) && - badoff < lp->args[0] + lp->args[1]) - prt("\t***WWWW"); + ret = op_map_write(); break; case OP_TRUNCATE: - down = lp->args[0] < lp->args[1]; - prt("TRUNCATE %s\tfrom 0x%x to 0x%x", - down ? "DOWN" : "UP", lp->args[1], lp->args[0]); - if (badoff >= lp->args[!down] && - badoff < lp->args[! !down]) - prt("\t******WWWW"); - break; - case OP_CLOSEOPEN: - prt("CLOSE/OPEN"); - break; - case OP_SKIPPED: - prt("SKIPPED (no operation)"); + ret = op_truncate(); break; + case OP_READ: default: - prt("BOGUS LOG ENTRY (operation code = %d)!", - lp->operation); - } - prt("\n"); - i++; - if (i == LOGSIZE) - i = 0; + ret = op_read(); + break; + }; + + if (ret == -1) + break; + + counter += ret; } + + if (counter != op_nums) + tst_brk(TFAIL, "Some file operations failed"); + else + tst_res(TPASS, "All file operations succeed"); } -void save_buffer(char *buffer, off_t bufferlength, int fd) +static void setup(void) { - off_t ret; - ssize_t byteswritten; + if (tst_parse_filesize(str_file_max_size, &file_max_size, 1, LLONG_MAX)) + tst_brk(TBROK, "Invalid file size '%s'", str_file_max_size); - if (fd <= 0 || bufferlength == 0) - return; + if (tst_parse_filesize(str_op_max_size, &op_max_size, 1, LLONG_MAX)) + tst_brk(TBROK, "Invalid maximum size for single operation '%s'", str_op_max_size); - if (bufferlength > INT_MAX) { - prt("fsx flaw: overflow in save_buffer\n"); - exit(67); - } - if (lite) { - off_t size_by_seek = lseek(fd, (off_t) 0, SEEK_END); - if (size_by_seek == (off_t) - 1) - prterr("save_buffer: lseek eof"); - else if (bufferlength > size_by_seek) { - warn("save_buffer: .fsxgood file too short... will " - "save 0x%llx bytes instead of 0x%llx\n", - (unsigned long long)size_by_seek, - (unsigned long long)bufferlength); - bufferlength = size_by_seek; - } - } + if (tst_parse_int(str_op_nums, &op_nums, 1, INT_MAX)) + tst_brk(TBROK, "Invalid number of operations '%s'", str_op_nums); - ret = lseek(fd, (off_t) 0, SEEK_SET); - if (ret == (off_t) - 1) - prterr("save_buffer: lseek 0"); + if (tst_parse_int(str_op_write_align, &op_write_align, 1, INT_MAX)) + tst_brk(TBROK, "Invalid memory write alignment factor '%s'", str_op_write_align); - byteswritten = write(fd, buffer, (size_t) bufferlength); - if (byteswritten != bufferlength) { - if (byteswritten == -1) - prterr("save_buffer write"); - else - warn("save_buffer: short write, 0x%x bytes instead " - "of 0x%llx\n", - (unsigned)byteswritten, - (unsigned long long)bufferlength); - } + if (tst_parse_int(str_op_read_align, &op_read_align, 1, INT_MAX)) + tst_brk(TBROK, "Invalid memory read alignment factor '%s'", str_op_read_align); + + if (tst_parse_int(str_op_trunc_align, &op_trunc_align, 1, INT_MAX)) + tst_brk(TBROK, "Invalid memory truncate alignment factor '%s'", str_op_trunc_align); + + page_size = (int)sysconf(_SC_PAGESIZE); + + srandom(time(NULL)); + + file_desc = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0666); + + file_buff = SAFE_MALLOC(file_max_size); + temp_buff = SAFE_MALLOC(file_max_size); } -void report_failure(int status) +static void cleanup(void) { - logdump(); + if (file_buff) + free(file_buff); - if (fsxgoodfd) { - if (good_buf) { - save_buffer(good_buf, file_size, fsxgoodfd); - prt("Correct content saved for comparison\n"); - prt("(maybe hexdump \"%s\" vs \"%s\")\n", - fname, goodfile); - } - close(fsxgoodfd); - } - exit(status); + if (temp_buff) + free(temp_buff); + + if (file_desc) + SAFE_CLOSE(file_desc); } -#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \ - *(((unsigned char *)(cp)) + 1))) - -void check_buffers(unsigned offset, unsigned size) -{ - unsigned char c, t; - unsigned i = 0; - unsigned n = 0; - unsigned op = 0; - unsigned bad = 0; - - if (memcmp(good_buf + offset, temp_buf, size) != 0) { - prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n", - offset, size); - prt("OFFSET\tGOOD\tBAD\tRANGE\n"); - while (size > 0) { - c = good_buf[offset]; - t = temp_buf[i]; - if (c != t) { - if (n == 0) { - bad = short_at(&temp_buf[i]); - prt("%#07x\t%#06x\t%#06x", offset, - short_at(&good_buf[offset]), bad); - op = temp_buf[offset & 1 ? i + 1 : i]; - } - n++; - badoff = offset; - } - offset++; - i++; - size--; - } - if (n) { - prt("\t%#7x\n", n); - if (bad) - prt("operation# (mod 256) for the bad data " - "may be %u\n", ((unsigned)op & 0xff)); - else - prt("operation# (mod 256) for the bad data " - "unknown, check HOLE and EXTEND ops\n"); - } else - prt("????????????????\n"); - report_failure(110); - } -} - -struct test_file { - char *path; - int fd; -} *test_files = NULL; - -int num_test_files = 0; -enum fd_iteration_policy { - FD_SINGLE, - FD_ROTATE, - FD_RANDOM, +static struct tst_test test = { + .needs_tmpdir = 1, + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .timeout = 1800, + .options = (struct tst_option[]) { + { "l:", &str_file_max_size, "Maximum size in MB of the test file(s) (default 262144)" }, + { "o:", &str_op_max_size, "Maximum size for single operation (default 65536)" }, + { "N:", &str_op_nums, "Total # operations to do (default 1000)" }, + { "w:", &str_op_write_align, "Write memory page alignment (default 1)" }, + { "r:", &str_op_read_align, "Read memory page alignment (default 1)" }, + { "t:", &str_op_trunc_align, "Truncate memory page alignment (default 1)" }, + {}, + }, }; -int fd_policy = FD_RANDOM; -int fd_last = 0; - -struct test_file *get_tf(void) -{ - unsigned index = 0; - - switch (fd_policy) { - case FD_ROTATE: - index = fd_last++; - break; - case FD_RANDOM: - index = random(); - break; - case FD_SINGLE: - index = 0; - break; - default: - prt("unknown policy"); - exit(1); - break; - } - return &test_files[index % num_test_files]; -} - -void assign_fd_policy(char *policy) -{ - if (!strcmp(policy, "random")) - fd_policy = FD_RANDOM; - else if (!strcmp(policy, "rotate")) - fd_policy = FD_ROTATE; - else { - prt("unknown -I policy: '%s'\n", policy); - exit(1); - } -} - -int get_fd(void) -{ - struct test_file *tf = get_tf(); - return tf->fd; -} - -void open_test_files(char **argv, int argc) -{ - struct test_file *tf; - int i; - - num_test_files = argc; - if (num_test_files == 1) - fd_policy = FD_SINGLE; - - test_files = calloc(num_test_files, sizeof(*test_files)); - if (test_files == NULL) { - prterr("reallocating space for test files"); - exit(1); - } - - for (i = 0, tf = test_files; i < num_test_files; i++, tf++) { - - tf->path = argv[i]; - tf->fd = open(tf->path, O_RDWR | (lite ? 0 : O_CREAT | O_TRUNC), - 0666); - if (tf->fd < 0) { - prterr(tf->path); - exit(91); - } - } - - if (quiet || fd_policy == FD_SINGLE) - return; - - for (i = 0, tf = test_files; i < num_test_files; i++, tf++) - prt("fd %d: %s\n", i, tf->path); -} - -void close_test_files(void) -{ - int i; - struct test_file *tf; - - for (i = 0, tf = test_files; i < num_test_files; i++, tf++) { - if (close(tf->fd)) { - prterr("close"); - report_failure(99); - } - } -} - -void check_size(void) -{ - struct stat statbuf; - off_t size_by_seek; - int fd = get_fd(); - - if (fstat(fd, &statbuf)) { - prterr("check_size: fstat"); - statbuf.st_size = -1; - } - size_by_seek = lseek(fd, (off_t) 0, SEEK_END); - if (file_size != statbuf.st_size || file_size != size_by_seek) { - prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n", - (unsigned long long)file_size, - (unsigned long long)statbuf.st_size, - (unsigned long long)size_by_seek); - report_failure(120); - } -} - -void check_trunc_hack(void) -{ - struct stat statbuf; - int fd = get_fd(); - - ftruncate(fd, (off_t) 0); - ftruncate(fd, (off_t) 100000); - if (fstat(fd, &statbuf)) { - prterr("trunc_hack: fstat"); - statbuf.st_size = -1; - } - if (statbuf.st_size != (off_t) 100000) { - prt("no extend on truncate! not posix!\n"); - exit(130); - } - ftruncate(fd, 0); -} - -static char *tf_buf = NULL; -static int max_tf_len = 0; - -void alloc_tf_buf(void) -{ - char dummy = '\0'; - int highest = num_test_files - 1; - int len; - - len = snprintf(&dummy, 0, "%u ", highest); - if (len < 1) { - prterr("finding max tf_buf"); - exit(1); - } - len++; - tf_buf = malloc(len); - if (tf_buf == NULL) { - prterr("allocating tf_buf"); - exit(1); - } - max_tf_len = snprintf(tf_buf, len, "%u ", highest); - if (max_tf_len < 1) { - prterr("fiding max_tv_len\n"); - exit(1); - } - if (max_tf_len != len - 1) { - warn("snprintf() gave %d instead of %d?\n", - max_tf_len, len - 1); - exit(1); - } -} - -char *fill_tf_buf(struct test_file *tf) -{ - if (tf_buf == NULL) - alloc_tf_buf(); - - sprintf(tf_buf, "%lu ", (unsigned long)(tf - test_files)); - return tf_buf; -} - -void -output_line(struct test_file *tf, int op, unsigned offset, - unsigned size, struct timeval *tv) -{ - char *tf_num = ""; - - char *ops[] = { - [OP_READ] = "read", - [OP_WRITE] = "write", - [OP_TRUNCATE] = "trunc from", - [OP_MAPREAD] = "mapread", - [OP_MAPWRITE] = "mapwrite", - }; - - /* W. */ - if (!(!quiet && ((progressinterval && - testcalls % progressinterval == 0) || - (debug && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))))) - return; - - if (fd_policy != FD_SINGLE) - tf_num = fill_tf_buf(tf); - - prt("%06lu %lu.%06lu %.*s%-10s %#08x %s %#08x\t(0x%x bytes)\n", - testcalls, tv->tv_sec, tv->tv_usec, max_tf_len, - tf_num, ops[op], - offset, op == OP_TRUNCATE ? " to " : "thru", - offset + size - 1, size); -} - -void doread(unsigned offset, unsigned size) -{ - struct timeval t; - off_t ret; - unsigned iret; - struct test_file *tf = get_tf(); - int fd = tf->fd; - - offset -= offset % readbdy; - gettimeofday(&t, NULL); - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size read\n"); - log4(OP_SKIPPED, OP_READ, offset, size, &t); - return; - } - if (size + offset > file_size) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping seek/read past end of file\n"); - log4(OP_SKIPPED, OP_READ, offset, size, &t); - return; - } - - log4(OP_READ, offset, size, 0, &t); - - if (testcalls <= simulatedopcount) - return; - - output_line(tf, OP_READ, offset, size, &t); - - ret = lseek(fd, (off_t) offset, SEEK_SET); - if (ret == (off_t) - 1) { - prterr("doread: lseek"); - report_failure(140); - } - iret = read(fd, temp_buf, size); - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu read done\n", t.tv_sec, t.tv_usec); - } - if (iret != size) { - if (iret == -1) - prterr("doread: read"); - else - prt("short read: 0x%x bytes instead of 0x%x\n", - iret, size); - report_failure(141); - } - check_buffers(offset, size); -} - -void domapread(unsigned offset, unsigned size) -{ - struct timeval t; - unsigned pg_offset; - unsigned map_size; - char *p; - struct test_file *tf = get_tf(); - int fd = tf->fd; - - offset -= offset % readbdy; - gettimeofday(&t, NULL); - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size read\n"); - log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t); - return; - } - if (size + offset > file_size) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping seek/read past end of file\n"); - log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t); - return; - } - - log4(OP_MAPREAD, offset, size, 0, &t); - - if (testcalls <= simulatedopcount) - return; - - output_line(tf, OP_MAPREAD, offset, size, &t); - - pg_offset = offset & page_mask; - map_size = pg_offset + size; - - if ((p = mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, - (off_t) (offset - pg_offset))) == MAP_FAILED) { - prterr("domapread: mmap"); - report_failure(190); - } - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec); - } - memcpy(temp_buf, p + pg_offset, size); - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec); - } - if (munmap(p, map_size) != 0) { - prterr("domapread: munmap"); - report_failure(191); - } - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec); - } - - check_buffers(offset, size); -} - -void gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size) -{ - while (size--) { - good_buf[offset] = testcalls % 256; - if (offset % 2) - good_buf[offset] += original_buf[offset]; - offset++; - } -} - -void dowrite(unsigned offset, unsigned size) -{ - struct timeval t; - off_t ret; - unsigned iret; - struct test_file *tf = get_tf(); - int fd = tf->fd; - - offset -= offset % writebdy; - gettimeofday(&t, NULL); - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size write\n"); - log4(OP_SKIPPED, OP_WRITE, offset, size, &t); - return; - } - - log4(OP_WRITE, offset, size, file_size, &t); - - gendata(original_buf, good_buf, offset, size); - if (file_size < offset + size) { - if (file_size < offset) - memset(good_buf + file_size, '\0', offset - file_size); - file_size = offset + size; - if (lite) { - warn("Lite file size bug in fsx!"); - report_failure(149); - } - } - - if (testcalls <= simulatedopcount) - return; - - output_line(tf, OP_WRITE, offset, size, &t); - - ret = lseek(fd, (off_t) offset, SEEK_SET); - if (ret == (off_t) - 1) { - prterr("dowrite: lseek"); - report_failure(150); - } - iret = write(fd, good_buf + offset, size); - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu write done\n", t.tv_sec, t.tv_usec); - } - if (iret != size) { - if (iret == -1) - prterr("dowrite: write"); - else - prt("short write: 0x%x bytes instead of 0x%x\n", - iret, size); - report_failure(151); - } -} - -void domapwrite(unsigned offset, unsigned size) -{ - struct timeval t; - unsigned pg_offset; - unsigned map_size; - off_t cur_filesize; - char *p; - struct test_file *tf = get_tf(); - int fd = tf->fd; - - offset -= offset % writebdy; - gettimeofday(&t, NULL); - if (size == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero size write\n"); - log4(OP_SKIPPED, OP_MAPWRITE, offset, size, &t); - return; - } - cur_filesize = file_size; - - log4(OP_MAPWRITE, offset, size, 0, &t); - - gendata(original_buf, good_buf, offset, size); - if (file_size < offset + size) { - if (file_size < offset) - memset(good_buf + file_size, '\0', offset - file_size); - file_size = offset + size; - if (lite) { - warn("Lite file size bug in fsx!"); - report_failure(200); - } - } - - if (testcalls <= simulatedopcount) - return; - - output_line(tf, OP_MAPWRITE, offset, size, &t); - - if (file_size > cur_filesize) { - if (ftruncate(fd, file_size) == -1) { - prterr("domapwrite: ftruncate"); - exit(201); - } - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 - || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu truncate done\n", t.tv_sec, - t.tv_usec); - } - } - pg_offset = offset & page_mask; - map_size = pg_offset + size; - - if ((p = - mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, - fd, (off_t) (offset - pg_offset))) == MAP_FAILED) { - prterr("domapwrite: mmap"); - report_failure(202); - } - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec); - } - memcpy(p + pg_offset, good_buf + offset, size); - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec); - } - if (msync(p, map_size, 0) != 0) { - prterr("domapwrite: msync"); - report_failure(203); - } - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu msync done\n", t.tv_sec, t.tv_usec); - } - if (munmap(p, map_size) != 0) { - prterr("domapwrite: munmap"); - report_failure(204); - } - if (!quiet && (debug > 1 && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend))))) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec); - } -} - -void dotruncate(unsigned size) -{ - struct timeval t; - int oldsize = file_size; - struct test_file *tf = get_tf(); - int fd = tf->fd; - - size -= size % truncbdy; - gettimeofday(&t, NULL); - if (size > biggest) { - biggest = size; - if (!quiet && testcalls > simulatedopcount) - prt("truncating to largest ever: 0x%x\n", size); - } - - log4(OP_TRUNCATE, size, (unsigned)file_size, 0, &t); - - if (size > file_size) - memset(good_buf + file_size, '\0', size - file_size); - file_size = size; - - if (testcalls <= simulatedopcount) - return; - - output_line(tf, OP_TRUNCATE, oldsize, size, &t); - - if (ftruncate(fd, (off_t) size) == -1) { - prt("ftruncate1: %x\n", size); - prterr("dotruncate: ftruncate"); - report_failure(160); - } - if (!quiet && debug > 1) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu trunc done\n", t.tv_sec, t.tv_usec); - } -} - -void writefileimage(void) -{ - ssize_t iret; - int fd = get_fd(); - - if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) - 1) { - prterr("writefileimage: lseek"); - report_failure(171); - } - iret = write(fd, good_buf, file_size); - if ((off_t) iret != file_size) { - if (iret == -1) - prterr("writefileimage: write"); - else - prt("short write: 0x%lx bytes instead of 0x%llx\n", - (unsigned long)iret, (unsigned long long)file_size); - report_failure(172); - } - if (lite ? 0 : ftruncate(fd, file_size) == -1) { - prt("ftruncate2: %llx\n", (unsigned long long)file_size); - prterr("writefileimage: ftruncate"); - report_failure(173); - } -} - -void docloseopen(void) -{ - struct timeval t; - struct test_file *tf = get_tf(); - - if (testcalls <= simulatedopcount) - return; - - gettimeofday(&t, NULL); - log4(OP_CLOSEOPEN, file_size, (unsigned)file_size, 0, &t); - - if (debug) - prt("%06lu %lu.%06lu close/open\n", testcalls, t.tv_sec, - t.tv_usec); - if (close(tf->fd)) { - prterr("docloseopen: close"); - report_failure(180); - } - if (!quiet && debug > 1) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu close done\n", t.tv_sec, t.tv_usec); - } - tf->fd = open(tf->path, O_RDWR, 0); - if (tf->fd < 0) { - prterr("docloseopen: open"); - report_failure(181); - } - if (!quiet && debug > 1) { - gettimeofday(&t, NULL); - prt(" %lu.%06lu open done\n", t.tv_sec, t.tv_usec); - } -} - -void test(void) -{ - unsigned long offset; - unsigned long size = maxoplen; - unsigned long rv = random(); - unsigned long op = rv % (3 + !lite + mapped_writes); - - /* turn off the map read if necessary */ - - if (op == 2 && !mapped_reads) - op = 0; - - if (simulatedopcount > 0 && testcalls == simulatedopcount) - writefileimage(); - - testcalls++; - - if (debugstart > 0 && testcalls >= debugstart) - debug = 1; - - if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) - prt("%lu...\n", testcalls); - - /* - * READ: op = 0 - * WRITE: op = 1 - * MAPREAD: op = 2 - * TRUNCATE: op = 3 - * MAPWRITE: op = 3 or 4 - */ - if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */ - dotruncate(random() % maxfilelen); - else { - if (randomoplen) - size = random() % (maxoplen + 1); - if (lite ? 0 : op == 3) - dotruncate(size); - else { - offset = random(); - if (op == 1 || op == (lite ? 3 : 4)) { - offset %= maxfilelen; - if (offset + size > maxfilelen) - size = maxfilelen - offset; - if (op != 1) - domapwrite(offset, size); - else - dowrite(offset, size); - } else { - if (file_size) - offset %= file_size; - else - offset = 0; - if (offset + size > file_size) - size = file_size - offset; - if (op != 0) - domapread(offset, size); - else - doread(offset, size); - } - } - } - if (sizechecks && testcalls > simulatedopcount) - check_size(); - if (closeprob && (rv >> 3) < (1 << 28) / closeprob) - docloseopen(); -} - -void cleanup(int sig) -{ - if (sig) - prt("signal %d\n", sig); - prt("testcalls = %lu\n", testcalls); - exit(sig); -} - -void usage(void) -{ - fprintf(stdout, "usage: %s", - "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m " - "start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t " - "truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] " - "[ -I random|rotate ] fname [additional paths to fname..]\n" - " -b opnum: beginning operation number (default 1)\n" - " -c P: 1 in P chance of file close+open at each op (default infinity)\n" - " -d: debug output for all operations [-d -d = more debugging]\n" - " -l flen: the upper bound on file size (default 262144)\n" - " -m start:end: monitor (print debug) specified byte range (default 0:infinity)\n" - " -n: no verifications of file size\n" - " -o oplen: the upper bound on operation size (default 65536)\n" - " -p progressinterval: debug output at specified operation interval\n" - " -q: quieter operation\n" - " -r readbdy: 4096 would make reads page aligned (default 1)\n" - " -s style: 1 gives smaller truncates (default 0)\n" - " -t truncbdy: 4096 would make truncates page aligned (default 1)\n" - " -w writebdy: 4096 would make writes page aligned (default 1)\n" - " -D startingop: debug output starting at specified operation\n" - " -L: fsxLite - no file creations & no file size changes\n" - " -N numops: total # operations to do (default infinity)\n" - " -O: use oplen (see -o flag) for every op (default random)\n" - " -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n" - " -S seed: for random # generator (default 1) 0 gets timestamp\n" - " -W: mapped write operations DISabled\n" - " -R: read() system calls only (mapped reads disabled)\n" - " -I: When multiple paths to the file are given each operation uses\n" - " a different path. Iterate through them in order with 'rotate'\n" - " or chose then at 'random'. (defaults to random)\n" - " fname: this filename is REQUIRED (no default)\n"); - exit(90); -} - -int getnum(char *s, char **e) -{ - int ret = -1; - - *e = NULL; - ret = strtol(s, e, 0); - if (*e) - switch (**e) { - case 'b': - case 'B': - ret *= 512; - *e = *e + 1; - break; - case 'k': - case 'K': - ret *= 1024; - *e = *e + 1; - break; - case 'm': - case 'M': - ret *= 1024 * 1024; - *e = *e + 1; - break; - case 'w': - case 'W': - ret *= 4; - *e = *e + 1; - break; - } - return (ret); -} - -int main(int argc, char **argv) -{ - int i, style, ch; - char *endp; - int dirpath = 0; - - goodfile[0] = 0; - logfile[0] = 0; - - page_size = getpagesize(); - page_mask = page_size - 1; - - setvbuf(stdout, NULL, _IOLBF, 0); /* line buffered stdout */ - - while ((ch = getopt(argc, argv, - "b:c:dl:m:no:p:qr:s:t:w:D:I:LN:OP:RS:W")) - != EOF) - switch (ch) { - case 'b': - simulatedopcount = getnum(optarg, &endp); - if (!quiet) - fprintf(stdout, "Will begin at operation" - "%ld\n", simulatedopcount); - if (simulatedopcount == 0) - usage(); - simulatedopcount -= 1; - break; - case 'c': - closeprob = getnum(optarg, &endp); - if (!quiet) - fprintf(stdout, - "Chance of close/open is 1 in %d\n", - closeprob); - if (closeprob <= 0) - usage(); - break; - case 'd': - debug++; - break; - case 'l': - maxfilelen = getnum(optarg, &endp); - if (maxfilelen <= 0) - usage(); - break; - case 'm': - monitorstart = getnum(optarg, &endp); - if (monitorstart < 0) - usage(); - if (!endp || *endp++ != ':') - usage(); - monitorend = getnum(endp, &endp); - if (monitorend < 0) - usage(); - if (monitorend == 0) - monitorend = -1; /* aka infinity */ - debug = 1; - case 'n': - sizechecks = 0; - break; - case 'o': - maxoplen = getnum(optarg, &endp); - if (maxoplen <= 0) - usage(); - break; - case 'p': - progressinterval = getnum(optarg, &endp); - if (progressinterval < 0) - usage(); - break; - case 'q': - quiet = 1; - break; - case 'r': - readbdy = getnum(optarg, &endp); - if (readbdy <= 0) - usage(); - break; - case 's': - style = getnum(optarg, &endp); - if (style < 0 || style > 1) - usage(); - break; - case 't': - truncbdy = getnum(optarg, &endp); - if (truncbdy <= 0) - usage(); - break; - case 'w': - writebdy = getnum(optarg, &endp); - if (writebdy <= 0) - usage(); - break; - case 'D': - debugstart = getnum(optarg, &endp); - if (debugstart < 1) - usage(); - break; - case 'I': - assign_fd_policy(optarg); - break; - case 'L': - lite = 1; - break; - case 'N': - numops = getnum(optarg, &endp); - if (numops < 0) - usage(); - break; - case 'O': - randomoplen = 0; - break; - case 'P': - strncpy(goodfile, optarg, sizeof(goodfile)); - strcat(goodfile, "/"); - strncpy(logfile, optarg, sizeof(logfile)); - strcat(logfile, "/"); - dirpath = 1; - break; - case 'R': - mapped_reads = 0; - break; - case 'S': - seed = getnum(optarg, &endp); - if (seed == 0) - seed = time(0) % 10000; - if (!quiet) - fprintf(stdout, "Seed set to %d\n", seed); - if (seed < 0) - usage(); - break; - case 'W': - mapped_writes = 0; - if (!quiet) - fprintf(stdout, "mapped writes DISABLED\n"); - break; - - default: - usage(); - - } - argc -= optind; - argv += optind; - if (argc < 1) - usage(); - fname = argv[0]; - - signal(SIGHUP, cleanup); - signal(SIGINT, cleanup); - signal(SIGPIPE, cleanup); - signal(SIGALRM, cleanup); - signal(SIGTERM, cleanup); - signal(SIGXCPU, cleanup); - signal(SIGXFSZ, cleanup); - signal(SIGVTALRM, cleanup); - signal(SIGUSR1, cleanup); - signal(SIGUSR2, cleanup); - - initstate(seed, state, 256); - setstate(state); - - open_test_files(argv, argc); - - strncat(goodfile, dirpath ? basename(fname) : fname, 256); - strcat(goodfile, ".fsxgood"); - fsxgoodfd = open(goodfile, O_RDWR | O_CREAT | O_TRUNC, 0666); - if (fsxgoodfd < 0) { - prterr(goodfile); - exit(92); - } - strncat(logfile, dirpath ? basename(fname) : fname, 256); - strcat(logfile, ".fsxlog"); - fsxlogf = fopen(logfile, "w"); - if (fsxlogf == NULL) { - prterr(logfile); - exit(93); - } - if (lite) { - off_t ret; - int fd = get_fd(); - file_size = maxfilelen = lseek(fd, (off_t) 0, SEEK_END); - if (file_size == (off_t) - 1) { - prterr(fname); - warn("main: lseek eof"); - exit(94); - } - ret = lseek(fd, (off_t) 0, SEEK_SET); - if (ret == (off_t) - 1) { - prterr(fname); - warn("main: lseek 0"); - exit(95); - } - } - original_buf = malloc(maxfilelen); - if (original_buf == NULL) - exit(96); - for (i = 0; i < maxfilelen; i++) - original_buf[i] = random() % 256; - - good_buf = malloc(maxfilelen); - if (good_buf == NULL) - exit(97); - memset(good_buf, '\0', maxfilelen); - - temp_buf = malloc(maxoplen); - if (temp_buf == NULL) - exit(99); - memset(temp_buf, '\0', maxoplen); - - if (lite) { /* zero entire existing file */ - ssize_t written; - int fd = get_fd(); - - written = write(fd, good_buf, (size_t) maxfilelen); - if (written != maxfilelen) { - if (written == -1) { - prterr(fname); - warn("main: error on write"); - } else - warn("main: short write, 0x%x bytes instead " - "of 0x%x\n", - (unsigned)written, maxfilelen); - exit(98); - } - } else - check_trunc_hack(); - - while (numops == -1 || numops--) - test(); - - close_test_files(); - prt("All operations completed A-OK!\n"); - - if (tf_buf) - free(tf_buf); - - free(original_buf); - free(good_buf); - free(temp_buf); - - return 0; -} diff --git a/testcases/kernel/fs/fsx-linux/fsxtest b/testcases/kernel/fs/fsx-linux/fsxtest deleted file mode 100755 index 61835437..00000000 --- a/testcases/kernel/fs/fsx-linux/fsxtest +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2001 Silicon Graphics, Inc. All Rights Reserved. -# Copyright (c) International Business Machines Corp., 2001 -# -# 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 -# -# -# -# Proper error checking and result reporting still needed -# -# usage: fsxtest $1 $2 $3 -# $1 scratch device to use for testing -# $2 optional file system type -# $3 number of operations to perform - -#Uncomment line below for debugging -#set -x -if [ $2 = "jfs" ]; then - mkfs -t $2 -q $1 -else - mkfs -t $2 $1 -fi -mkdir /testmount -mount -t $2 $1 /testmount -touch /testmount/testfile -fsx-linux -N $3 /testmount/testfile -RESULT=$? -# report the results -if [ $RESULT -eq "0" ]; then - echo "PASS: fsxtest $1 $2 $3" -else - echo "FAIL: fsxtest $1 $2 $3" -fi -umount /testmount -rm -rf /testmount -fsck -a -t $2 $1 # report the results -exit $RESULT diff --git a/testcases/kernel/fs/fsx-linux/fsxtest02 b/testcases/kernel/fs/fsx-linux/fsxtest02 deleted file mode 100755 index fe014abd..00000000 --- a/testcases/kernel/fs/fsx-linux/fsxtest02 +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2001 Silicon Graphics, Inc. All Rights Reserved. -# Copyright (c) International Business Machines Corp., 2001 -# -# 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 -# -# -# -# Proper error checking and result reporting still needed -# -# usage: fsxtest02 $1 -# $1 number of operations to perform - -#Uncomment line below for debugging -#set -x - -TCbin=${TCbin:-`pwd`} -TCtmp=${TCtmp:-/tmp/fsxtest2.$$} - -mkdir -p $TCtmp 2>/dev/null -touch $TCtmp/testfile -$TCbin/fsx-linux -N $1 $TCtmp/testfile -RESULT=$? -# report the results -if [ $RESULT -eq "0" ]; then - echo "PASS: fsxtest02 $1 $2 $3" -else - echo "FAIL: fsxtest02 $1 $2 $3" -fi -rm -rf $TCtmp -exit $RESULT diff --git a/testcases/kernel/fs/iso9660/isofs.sh b/testcases/kernel/fs/iso9660/isofs.sh index dfa4ac73..088e062d 100755 --- a/testcases/kernel/fs/iso9660/isofs.sh +++ b/testcases/kernel/fs/iso9660/isofs.sh @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) International Business Machines Corp., 2003 -# Copyright (c) Linux Test Project, 2016-2021 +# Copyright (c) Linux Test Project, 2016-2024 # Written by Prakash Narayana (prakashn@us.ibm.com) # and Michael Reed (mreed10@us.ibm.com) # @@ -11,21 +11,19 @@ TST_NEEDS_CMDS="mount umount" TST_NEEDS_TMPDIR=1 -TST_SETUP=setup TST_TESTFUNC=do_test +TST_CNT=3 +TST_SETUP="setup" MAX_DEPTH=3 MAX_DIRS=4 +TEST_USER='nobody' + setup() { - if tst_cmd_available mkisofs; then - MKISOFS_CMD="mkisofs" - elif tst_cmd_available genisoimage; then - MKISOFS_CMD="genisoimage" - else - tst_brk TCONF "please install mkisofs or genisoimage" - fi + TEST_GROUP="$(id -g -n $TEST_USER)" + [ "$TEST_GROUP" ] || TEST_GROUP="$TEST_USER" } gen_fs_tree() @@ -50,10 +48,33 @@ do_test() local make_file_sys_dir="$PWD/files" local mkisofs_opt mount_opt + case $1 in + 1) MKISOFS_CMD="mkisofs" + HFSOPT="-hfs -D" + GREPOPT="mkisofs";; + 2) MKISOFS_CMD="genisoimage" + HFSOPT="-hfsplus -D -hfs -D" + GREPOPT="genisoimage";; + 3) MKISOFS_CMD="xorrisofs" + HFSOPT="-hfsplus -D" + GREPOPT="xorriso";; + esac + + if ! tst_cmd_available $MKISOFS_CMD; then + tst_res TCONF "Missing '$MKISOFS_CMD'" + return + fi + + if ! $MKISOFS_CMD 2>&1 | head -n 2 | grep -q "$GREPOPT"; then + tst_res TCONF "'$MKISOFS_CMD' is a symlink to another tool" + return + fi + + tst_res TINFO "Testing $MKISOFS_CMD" + mkdir -p -m 777 $mnt_point mkdir -p $make_file_sys_dir - # Generated directories and files mkdir -p $make_file_sys_dir gen_fs_tree "$make_file_sys_dir" 1 @@ -62,15 +83,16 @@ do_test() for mkisofs_opt in \ " " \ "-J" \ - "-hfs -D" \ + "$HFSOPT" \ " -R " \ "-R -J" \ "-f -l -D -J -allow-leading-dots -R" \ - "-allow-lowercase -allow-multidot -iso-level 3 -f -l -D -J -allow-leading-dots -R" + "-allow-lowercase -allow-multidot -iso-level 3 -f -l -D -J \ + -allow-leading-dots -R" do rm -f isofs.iso - EXPECT_PASS $MKISOFS_CMD -o isofs.iso -quiet $mkisofs_opt $make_file_sys_dir 2\> /dev/null \ - || continue + EXPECT_PASS $MKISOFS_CMD -o isofs.iso -quiet $mkisofs_opt \ + $make_file_sys_dir 2\> /dev/null || continue for mount_opt in \ "loop" \ @@ -79,8 +101,8 @@ do_test() "loop,block=512,unhide" \ "loop,block=1024,cruft" \ "loop,block=2048,nocompress" \ - "loop,check=strict,map=off,gid=bin,uid=bin" \ - "loop,check=strict,map=acorn,gid=bin,uid=bin" \ + "loop,check=strict,map=off,gid=$TEST_GROUP,uid=$TEST_USER" \ + "loop,check=strict,map=acorn,gid=$TEST_GROUP,uid=$TEST_USER" \ "loop,check=relaxed,map=normal" \ "loop,block=512,unhide,session=2" do diff --git a/testcases/kernel/fs/mongo/README b/testcases/kernel/fs/mongo/README deleted file mode 100755 index a134305b..00000000 --- a/testcases/kernel/fs/mongo/README +++ /dev/null @@ -1,37 +0,0 @@ - - MONGO.PL BENCHMARK. - -To run mongo benchmark please use the next : - -# run_mongo - -Where : - - test device - - number of processes - -The benchmark will be performed on given device with -reiserfs and ext2. Then results will be compared. - -The results directory : ./results -The comparision *.html and *.txt files in ./results/html - -Warning : All info will be erased on device. - ------------------------------------------------------- - -Mongo.pl description : - - mongo.pl - - for example : - mongo.pl reiserfs /dev/hda5 /testfs log 1 - -Be careful : - /dev/hda5 - test device and all info on it will be erased. - It should be at least 500 Mb in size. - - /testfs - mount-point directory - - log - name prefix for results file. - - diff --git a/testcases/kernel/fs/mongo/map5.c b/testcases/kernel/fs/mongo/map5.c deleted file mode 100755 index 7cd700e9..00000000 --- a/testcases/kernel/fs/mongo/map5.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README - */ - -#include -#include -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - int fd; - int block; - int first_block; - int last_block; - int totals_block; - int fragments; - int i; - int n; - - for (n = 1; n < argc; n++) { - if (argc < 2) { - printf - ("Used to see file maps \nUsage: %s filename1 [[..[filename2]...filename(N-1)] filenameN]\n", - argv[0]); - return 0; - } - fd = open(argv[n], O_RDONLY); - if (fd == -1) { - perror("open failed"); - continue; - } - // printf ("file %s occupies blocks: \n", argv[1]); - // printf ("START\tEND\tCOUNT\n"); - i = 0; - block = 0; - first_block = 0; - last_block = 0; - fragments = 0; - totals_block = 0; - - while (ioctl(fd, FIBMAP, &block) == 0) { - if (first_block == 0) { - last_block = block - 1; - first_block = block; - } - if (block != last_block + 1) { - // printf ("%d\t%d\t%d\n",first_block,last_block,last_block-first_block+1); - totals_block += last_block - first_block + 1; - fragments++; - first_block = block; - last_block = block; - } else { - last_block++; - } - - if (!block) { - //printf ("Fragments: %d\tBlocks: %d\n",fragments,totals_block); - //printf ("%d:%d\t",fragments,totals_block); - //if (fragments == 1) printf(".",totals_block); - //else printf("%d_",fragments,totals_block); - printf("%d\n", fragments); - break; - } - - i++; - block = i; - } - if (errno) { - perror("FIBMAP failed"); - } - close(fd); - // printf ("\n"); - } - return 0; -} diff --git a/testcases/kernel/fs/mongo/mongo.pl b/testcases/kernel/fs/mongo/mongo.pl deleted file mode 100755 index 5d47d874..00000000 --- a/testcases/kernel/fs/mongo/mongo.pl +++ /dev/null @@ -1,511 +0,0 @@ -#!/usr/bin/perl -# -# Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README -# - -# -# Mongo.pl is reiserfs benchmark. -# -# To run please use run_mongo script or : -# -# # ./mongo.pl reiserfs /dev/xxxx /testfs log1 5 -# or -# # ./mongo.pl ext2 /dev/xxxx /testfs log2 5 -# -# 5 - number of processes, you can set any number here -# -# Test will format partition /dev/xxxx by 'mkreiserfs' or 'mke2fs' -# mount it and run given number of processes during each phase : -# Create, Copy, Symlinks, Read, Stats, Rename and Delete. -# -# Also, the program calc fragmentations after Create and Copy phases: -# Fragm = number_of_fragments / number_of_files -# (Current version use the files more than 16KB to calc Fragm.) -# -# You can find the same results in files : log, log.tbl, log_table -# -# log - raw results -# log.tbl - results for compare program -# log_table - results in table form -# - -$EXTENDED_STATISTICS = 1; - - -use POSIX; -use File::stat; - -sub print_usage { - - print "\nUsage: mongo.pl "; - print " \n"; - - print " - the name of filesystem [reiserfs|ext2]\n"; - print " - the device for benchmark (e.g. /dev/hda9)\n"; - print " - mount-point for the filesystem"; - print " (e.g. /mnt/testfs)\n"; - print " - the name prefix for benchmark results\n"; - print " - the number of benchmark processes\n"; - - print "\nexamples:\n"; - print "mongo.pl reiserfs /dev/hda9 /testfs reiserfs_results 1\n"; - print "mongo.pl ext2 /dev/hda9 /testfs ext2_results 1\n"; - - print "\nThe results will be put in ./results directory\n"; -} - - -#------- Subroutines declaration -------- -sub make_fsys; -sub mongo_x_process; -sub mongo_launcher; -sub set_params; - -#------- main() ------------------------- - -if ( $#{ARGV} != 4 ) { - print_usage; - exit(0); - } - -#-------------------------------------------- -# Set working directories -#-------------------------------------------- -$TOPDIR = "$ENV{PWD}"; - -$RESDIR = "${TOPDIR}/results"; -$HTMLDIR = "${RESDIR}/html"; - -$FILESYSTEM = $ARGV[0]; -$DEVICE = $ARGV[1]; -$TESTDIR = $ARGV[2]; -$PROCESSES = $ARGV[4]; - -$LOGFILE = "${RESDIR}/${ARGV[3]}"; -$LOGFILE2 = "${LOGFILE}_table"; -$LOGFILE3 = "${LOGFILE}.tbl"; - -$TMPFILE = "${RESDIR}/mongo_tmp"; -$nproc = $PROCESSES; -$READIT = "${TOPDIR}/mongo_read"; -$SLINKS = "${TOPDIR}/mongo_slinks"; - -#-------- reiser_fract_tree parameters---------------- -$x1mb = 1024 * 1024; -$x2mb = 2 * $x1mb; -$x3mb = 3 * $x1mb; - -$x5mb = 5 * $x1mb; -$x50mb = 50 * $x1mb; -$x100mb = 100 * $x1mb; - -# Total amount of bytes in all files on test partition -#----------------------------------------------------- - -$small_bytes = $x50mb; -$medium_bytes = $x100mb; -$big_bytes = $x100mb; -$large_bytes = $x100mb; - -# Median size of files in bytes for first tree to create -#------------------------------------------------------- -$small_size = 100; -$medium_size = 1000; -$big_size = 10000; -$large_size = 100000; - -# Keep the largest file to one fifth (100 million bytes) -# of the total tree size. -#------------------------------------------------------- -$max_file_size = 100000000; - -# Yuri Shevchuk says that 0 is the median size -# in real life, so I believe him. -#---------------------------------------------- -$median_dir_nr_files = 0; - -# This should be larger, change once done testing. -#------------------------------------------------- -$bytes_to_consume = 10000000; - -$median_file_size = 100; -$max_file_size = 1000000; - -$median_dir_nr_files = 100; -$max_directory_nr_files = 10000; - -$median_dir_branching = 0; -$max_dir_branching = 1; - -# This should be varying, someday.... -#------------------------------------ -$write_buffer_size = 4096; - -@numb_of_bytes = ($small_bytes, $medium_bytes, $big_bytes, $large_bytes); -@size_of_files = ($small_size, $medium_size, $big_size, $large_size); - -$reiser_fract_tree_rep_counter = 3; - -$total_params = $#{numb_of_bytes}; - -#... Make directories for results -#-------------------------------- -unless (-e $RESDIR) { - print "Creating dir: ${RESDIR} \n"; - system("mkdir $RESDIR"); -} - -unless ( -e $HTMLDIR ) { - print "Creating dir: ${HTMLDIR} \n"; - system("mkdir $HTMLDIR"); -} - -#... Compile *.c files if it is necessary -#---------------------------------------- -sub compile -{ - my $file = shift @_; - my $opt = shift @_ if @_ ; - my $cfile = $file . ".c"; - die "source file \"${cfile}\" does not exist" unless (-e "$cfile"); - if ( -e "$file" && (stat("$file")->mtime >= stat("$cfile")->mtime)) { - print "$file is up to date ...\n"; - } else { - print "Compiling ${cfile} ...\n"; - system ("gcc $cfile -o $file $opt"); - } -} - -compile("reiser_fract_tree", "-lm"); -compile("mongo_slinks"); -compile("mongo_read"); -compile("map5"); -compile("summ"); -compile("mongo_compare"); - -#... Check the command string parameters -#--------------------------------------- -unless ( ($FILESYSTEM eq "reiserfs") or ($FILESYSTEM eq "ext2") ) { - print "mongo.pl: not valid filesystem name: ${FILESYSTEM} \n"; - print "Usage: mongo.pl \n"; - exit(0); -} - -unless ( -b $DEVICE ) { - print "mongo.pl: not valid device: ${DEVICE} \n"; - print "Usage: mongo.pl \n"; - exit(0); -} - - -#------- Subroutines -------------------------------------- -#---------------------------------------------------------- -sub get_blocks_usage ($) { - my ($mp) = @_; - my $df = `df -k $mp | tail -n 1`; - chomp $df; - my @items = split / +/, $df; - return $items[2]; -} - -sub make_fsys { - - system ("umount $TESTDIR") ; - - if ( $FILESYSTEM eq "reiserfs" ) { - system("echo y | mkreiserfs $DEVICE") ; - system("mount -t reiserfs $DEVICE $TESTDIR") ; - } - - if ( $FILESYSTEM eq "ext2" ) { - system("mke2fs $DEVICE") ; - system("mount $DEVICE $TESTDIR") ; - } -} - - -#------------------------------------------------------------------ -# Mongo Launcher -#------------------------------------------------------------------ -sub mongo_launcher { - - my ($phase_num, $phase_name, $cmd, $dir1, $dir2, $flag, $processes) = @_ ; - - - print "$phase_num.$phase_name files of median size $median_file_size bytes ($p processes)...\n"; - print LOG "********* Phase $phase_num: $phase_name files of median size $median_file_size bytes ($p processes) *********\n"; - $i=0; - $total=0; - -# eliminate the rep counter and the while - while ( $i < $reiser_fract_tree_rep_counter ) { - print "$phase_name : "; - print LOG "$phase_name : "; - $com = ""; - $pp=$processes; - - $j=0; - while ($pp > 0) { - $pp--; - -# the fact that this if statement was necessary indicated you -# attempted excessive generalization and abstraction where it was not -# natural to the task that makes the code harder to understand. put -# every command on one line to execute. I like it when I can read a -# one line command and see what that phase of the test does instead of -# looking in many places throughout the code. - - if ($phase_num == 1) { - $com .= "$cmd $dir1-$i-$j $flag"; - } - elsif ($phase_num == 2) { - $com .= "$cmd $dir1-$i-$j $dir2-$i-$j"; - } - elsif ($phase_num == 3) { - $com .= "$cmd $dir1-$i-$j "."-type f | while read X; do echo \$X \$X.lnk ; done | $TOPDIR/mongo_slinks "; - } - elsif ($phase_num == 4) { - $com .= "$cmd"; - } - elsif ($phase_num == 5) { - $com .= "$cmd"; - } - elsif ($phase_num == 6) { - $com .= "$cmd $dir1-$i-$j -type f | perl -e 'while (<>) { chomp; rename (\$_, \"\$_.r\"); };'"; - #$com .= " & $cmd $dir2-$i-$j "."-type d -exec mv {} {}.r ';'"; - } - elsif ($phase_num == 7) { - if ($processes > 1) { - $com .= "$cmd $dir1-$i-$j & $cmd $dir2-$i-$j"; - } - else { - $com .= "$cmd $dir1-$i-$j ; $cmd $dir2-$i-$j"; - } - } - $com .= " & "; - $j++; - } - - $com .= " wait"; - #print $com, "\n"; - - @t=`(time -p $com) 2>&1`; - - @tt = split ' ', $t[0]; - $res = $tt[1]; - unless ( $res =~ /\s*\d+/) { - print @t , "\n"; - print LOG @t, "\n"; - } else { - print LOG "$res sec.\n"; - print "$res sec.\n"; - } - - $total += $res; - $i++; - } - - print "total $phase_name time: $total sec.\n"; - print LOG "total $phase_name time: $total sec.\n"; - - $ares[$phase_num]=$total; # ser array of results - - if ($EXTENDED_STATISTICS) { - if( $phase_num < 3) { - $used = get_blocks_usage($TESTDIR) - $used0; - if ($phase_num == 1) { - $used1=$used; - }elsif($phase_num == 2){ - $used2=$used; - } - print "Used disk space (df) : $used KB\n"; - print LOG "Used disk space (df) : $used KB\n"; - - open (FIND_PIPE, "find $TESTDIR|") || die "cannnot open pipe from \"find\": $!\n"; - $dirs = 0; - $files = 0; - $files16 = 0; - - while() { - chomp; - $st = lstat ($_); - if (S_ISDIR($st->mode)) { - $dirs ++; - } elsif (S_ISREG($st->mode)) { - $files ++; - $files16 ++ if ($st->size > 16384); - } - } - - close (FIND_PIPE); - - print "Total dirs: $dirs\n"; - print "Total files: $files\n"; - print LOG "Total dirs: $dirs\n"; - print LOG "Total files: $files\n"; - - #$f=$frag; - $f16 = $files16; - $fr16 =`find $TESTDIR -type f -size +16k | xargs $TOPDIR/map5 | $TOPDIR/summ | tail -n 1 2>&1`; - @ff16= split ' ', $f16; - @ffr16= split ' ', $fr16; - $files16 = $ff16[0]; - $frag = $ffr16[0]; - $procent = $frag / $files16; - print "Total fragments : $frag \n"; - print LOG "Total fragments : $frag \n"; - - printf "Fragments / files :%.3f\n", $procent; - printf LOG "Fragments / files :%.3f\n", $procent; - $frag_res[$phase_num]=$procent; # ser array of results - } - } - - system("sync"); - print "\n"; - print LOG "\n"; - -} - -# and what is an x process? - -#------------------------------------------------------------------ -# MONGO_X_PROCESS ( x is number of processes to run ) -#------------------------------------------------------------------ -sub mongo_x_process { - - my ($processes) = @_ ; - $p = $processes; - - make_fsys; # make and mount the file system - $used0 = get_blocks_usage($TESTDIR); - - open LOG, ">>$LOGFILE" or die "Can not open log file $LOGFILE\n"; - open LOG2, ">>$LOGFILE2" or die "Can not open log file $LOGFILE2\n"; - open LOG3, ">>$LOGFILE3" or die "Can not open log file $LOGFILE2\n"; - - print LOG "FILESYSTEM=$FILESYSTEM \n"; - - print "\n"; - if($p == 1) { - print "mongo_single_process, the_set_of_param.N=$par_set_n of $total_params \n"; - print LOG "mongo_single_process, the_set_of_paramN=$par_set_n of $total_params \n"; - } elsif ($p > 1) { - print "mongo_multi_process ($p processes), the_set_of_param.N=$par_set_n of $total_params \n"; - print LOG "mongo_multi_process ($p processes), the_set_of_paramN=$par_set_n of $total_params \n"; - } - - print "Results in file : $LOGFILE \n"; - print "\n"; - - $dir1 = "$TESTDIR/testdir1"; - $dir2 = "$TESTDIR/testdir2"; - $flag = 0; - - $cmd_1 = "$TOPDIR/reiser_fract_tree $bytes_to_consume $median_file_size $max_file_size $median_dir_nr_files $max_directory_nr_files $median_dir_branching $max_dir_branching $write_buffer_size"; - $cmd_2 = "cp -r"; - $cmd_3 = "find"; - $cmd_4 = "find $TESTDIR -type f | xargs $TOPDIR/mongo_read"; - $cmd_5 = "find $TESTDIR -type f > /dev/null"; # it should be enough for stat all files. -zam - $cmd_6 = "find"; #" $TESTDIR -type f -exec mv {} {}.r ';'"; - $cmd_7 = "rm -r"; - - system("sync"); - $frag = 0; - mongo_launcher ( 1, "Create", $cmd_1, $dir1, $dir2, $flag, $p); # phase 1 - mongo_launcher ( 2, "Copy ", $cmd_2, $dir1, $dir2, $flag, $p); # phase 2 - mongo_launcher ( 3, "Slinks", $cmd_3, $dir1, $dir2, $flag, $p); # phase 3 - mongo_launcher ( 4, "Read ", $cmd_4, $dir1, $dir2, $flag, $p); # phase 4 - mongo_launcher ( 5, "Stats ", $cmd_5, $dir1, $dir2, $flag, $p); # phase 5 - mongo_launcher ( 6, "Rename", $cmd_6, $dir1, $dir2, $flag, $p); # phase 6 - mongo_launcher ( 7, "Delete", $cmd_7, $dir1, $dir2, $flag, $p); # phase 7 - - print LOG2 "\n"; - if ($processes > 1) { - print LOG2 "MONGO_MULTI_PROCESS ($processes processes) BENCHMARK RESULTS (time in sec.)\n"; - }else { - print LOG2 "MONGO_SINGLE_PROCESS BENCHMARK RESULTS (time in sec.)\n"; - } - print LOG2 " FILESYSTEM=$FILESYSTEM\n"; - print LOG2 " parameters: files=$files, base_size=$median_file_size bytes, dirs=$dirs\n"; - print LOG2 "--------------------------------------------------------------\n"; - print LOG2 "Create\tCopy\tSlink\tRead\tStats\tRename\tDelete\n"; - print LOG2 " time \ttime\ttime\ttime\ttime \t time \t time\n"; - print LOG2 "--------------------------------------------------------------\n"; - print LOG2 "$ares[1]\t$ares[2]\t$ares[3]\t$ares[4]\t$ares[5]\t$ares[6]\t$ares[7]\n"; - print LOG2 "--------------------------------------------------------------\n"; - print LOG2 "The size of files tree : \n"; - print LOG2 " after create = $used1 kb\n"; - print LOG2 " after copy = $used2 kb\n"; - print LOG2 "\n"; - - - print LOG3 "\n"; - if ($processes > 1) { - print LOG3 "MONGO_MULTI_PROCESS ($processes) \n"; - }else { - print LOG3 "MONGO_SINGLE_PROCESS \n"; - } - print LOG3 "parameters: \n"; - print LOG3 "files=$files \n"; - print LOG3 "base_size=$median_file_size bytes \n"; - print LOG3 "dirs=$dirs \n"; - print LOG3 "\n"; - - print LOG3 "FSYS=$FILESYSTEM \n"; - print LOG3 "(time in sec.) \n"; - print LOG3 "Create : $ares[1]\n"; - print LOG3 "Fragm. : $frag_res[1]\n"; - print LOG3 "df : $used1\n\n"; - print LOG3 "Copy : $ares[2] \n"; - print LOG3 "Fragm. : $frag_res[2]\n"; - print LOG3 "df : $used2\n\n"; - print LOG3 "Slinks : $ares[3]\n"; - print LOG3 "Read : $ares[4]\n"; - print LOG3 "Stats : $ares[5]\n"; - print LOG3 "Rename : $ares[6] \n"; - print LOG3 "Delete : $ares[7]\n"; - - print LOG3 "\n"; - - - if($processes > 1) { - print LOG "******* The end of mongo_multi_process *******"; - }else { - print LOG "******* The end of mongo_single_process *******"; - } -} - -#--------------------------------------------------- -# Set parameters -#--------------------------------------------------- -sub set_params { - my ($n) = @_ ; - - $bytes_to_consume = $numb_of_bytes[$n]; - $median_file_size = $size_of_files[$n]; - - #$max_file_size = 1000000; - - #$median_dir_nr_files = 100; - #$max_directory_nr_files = 10000; - - #$median_dir_branching = 0; - #$max_dir_branching = 1; - -} - -#---------------------------------------------------------- -# TEST START -#---------------------------------------------------------- - - $par_set_n = 0; - foreach $fsize (@size_of_files) { - set_params ($par_set_n); - mongo_x_process( $nproc ); # run n processes - $par_set_n++; - } - system("umount $TESTDIR"); - exit; - - diff --git a/testcases/kernel/fs/mongo/mongo_compare.c b/testcases/kernel/fs/mongo/mongo_compare.c deleted file mode 100755 index 6dba9534..00000000 --- a/testcases/kernel/fs/mongo/mongo_compare.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README - */ - -#include -#include -#include - -char time_str1[50]; -char time_str2[50]; -char name_str1[50]; -char tmp_str[20][100]; - -char out1[256]; -char out2[256]; - -FILE *f1; -FILE *f2; -FILE *f3; -FILE *f4; - -void write_html_head(FILE * fp); -void write_html_end(FILE * fp); - -char head_str[] = "\n \ -\n \ -\n \ -\n \ - \n \ - \n \ -\n \ -\n \ -"; -/* - \n \ -\n \ -\n \ -
\n \ -"; -*/ - -char end_str[] = "\n \ -
\n \ - \n \ - \n \ - \n \ -"; - -main(int argc, char **argv) -{ - float n1, n2, ratio; - char *p, *p1, *p2; - char line0[100]; - char line1[100]; - char line2[100]; - char line3[100]; - char out_line[100]; - char html_line[500]; - int i, k; - - if (argc < 3) { - printf("\nUsage: mongo_compare file1 file2 res_file\n\n"); - printf - ("\t should contain reiserfs or ext2 results of mogo benchmark\n"); - printf - ("\t should contain reiserfs or ext2 results of mogo benchmark\n"); - printf("\tMongo results will be compared\n"); - printf - ("\t will be contain results in the text form\n"); - printf - ("\t will be contain results in the html form\n"); - exit(0); - } - - strcpy(out1, argv[3]); - strcat(out1, ".txt"); - - strcpy(out2, argv[3]); - strcat(out2, ".html"); - - if ((f1 = fopen(argv[1], "r")) == NULL) { - fprintf(stderr, "%s: can't open %s\n", argv[0], argv[1]); - return 1; - } - - if ((f2 = fopen(argv[2], "r")) == NULL) { - fprintf(stderr, "%s: can't open %s\n", argv[0], argv[2]); - return 1; - } - - if ((f3 = fopen(out1, "wr")) == NULL) { - fprintf(stderr, "%s: can't open %s\n", argv[0], out1); - return 1; - } - - if ((f4 = fopen(out2, "wr")) == NULL) { - fprintf(stderr, "%s: can't open %s\n", argv[0], out2); - return 1; - } - - write_html_head(f4); - i = 0; - while (fgets(line1, 100, f1)) { - fgets(line2, 100, f2); - - if (p = strstr(line1, "\n")) - *(p + 1) = 0; - if (p = strstr(line2, "\n")) - *(p + 1) = 0; - - strcpy(line3, line1); - line3[strlen(line3) - 1] = 0; - - while (strlen(line3) < 40) { - strcat(line3, " "); - } - - if (strstr(line3, "MONGO_")) { - fprintf(f4, "\n\n"); - fprintf(f4, ""); - fprintf(f4, "\n \n"); - - line2[strlen(line2) - 1] = 0; - while (strlen(line2) < 40) { - strcat(line2, " "); - } - - strcat(line3, line2); - - strcpy(out_line, line3); - strcat(out_line, "\n"); - name_str1[0] = 0; - - if (p1 = strstr(line1, " :")) { - strcpy(time_str1, p1 + 2); - strncpy(name_str1, line1, p1 - line1); - - if (p2 = strstr(line2, " :")) { - strcpy(time_str2, p2 + 2); - - time_str1[strlen(time_str1) - 1] = 0; - time_str2[strlen(time_str2) - 1] = 0; - - sscanf(time_str1, "%f", &n1); - sscanf(time_str2, "%f", &n2); - - ratio = n1 / n2; - sprintf(out_line, "%s : %6.2f / %6.2f = %.2f\n", - name_str1, n1, n2, ratio); - - fprintf(f4, - "\n", - name_str1, n1, n2, ratio); - - } - } - - fprintf(f3, "%s", out_line); - line1[0] = 0; - line2[0] = 0; - line3[0] = 0; - out_line[0] = 0; - } - - write_html_end(f4); - - fclose(f1); - fclose(f2); - - fclose(f3); - fclose(f4); - - fflush(f3); - fflush(f4); -} - -/*******************************************/ -void write_html_head(FILE * fp) -{ - fprintf(fp, "%s", head_str); -} - -/*******************************************/ -void write_html_end(FILE * fp) -{ - fprintf(fp, "%s", end_str); -} diff --git a/testcases/kernel/fs/mongo/mongo_read.c b/testcases/kernel/fs/mongo/mongo_read.c deleted file mode 100755 index 927b92a1..00000000 --- a/testcases/kernel/fs/mongo/mongo_read.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README - */ - -/* - * MONGO READ - simple possible program to read a number of given files - * suitable for benchmarking FS read performance - */ - -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - int fd, rd, i; - char *buf; - int bufsize = 4096; - - if (argc < 2) { - printf("\nUsage: %s filename [,filename2 [,...] ] ]\n\n", - argv[0]); - return 0; - } - - buf = malloc(bufsize); - if (buf == 0) { - printf("Malloc failed on %d\n", bufsize); - return 0; - } - - /* Read all given files */ - for (i = 1; i < argc; i++) { - - /* open the file */ - fd = open(argv[i], O_RDONLY); - if (fd == -1) { - printf("Open failed (%s)\n", strerror(errno)); - return 0; - } - - /* read the file */ - while ((rd = read(fd, buf, bufsize)) == bufsize) ; - if (rd == -1) { - printf("Read failed (%s)\n", strerror(errno)); - return 0; - } - close(fd); - } - - free(buf); - return 0; -} diff --git a/testcases/kernel/fs/mongo/mongo_slinks.c b/testcases/kernel/fs/mongo/mongo_slinks.c deleted file mode 100755 index 6bd1e2b6..00000000 --- a/testcases/kernel/fs/mongo/mongo_slinks.c +++ /dev/null @@ -1,98 +0,0 @@ -// -// A simple symlink test -// - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include - -// -// Creates symlink [new-path] to [old-path], checks it, -// returnes 0 - if everything looks fine and -// 1 - otherwise. -// mongo_slinks reads arguments from stdin. - -int main(int argc, char *argv[]) -{ - char *old_path; - char *new_path; - - struct stat statbuf; - - int num; - char *buffer = NULL; - char *line_buffer = NULL; - size_t line_buffer_size = 0; - int size = 1; - - if ((buffer = malloc(size + 1)) == NULL) { - perror("checklink: malloc failed"); - return 1; - } - - while (getline(&line_buffer, &line_buffer_size, stdin) != -1) { - - old_path = strtok(line_buffer, "\t "); - new_path = strtok(NULL, "\t\n "); - - if (!old_path || !new_path) /* empty lines at the end of file */ - break; - - // Create symlink - if (symlink(old_path, new_path) != 0) { - perror("checklink : symlink failed "); - return 1; - } - // stat data of symlink itself - if (lstat(new_path, &statbuf) == -1) { - perror("checklink: lstat failed"); - return 1; - } - - if (!(S_ISLNK(statbuf.st_mode))) { - printf("checklink : file %s is not a symbol link\n", - new_path); - return 1; - } - // Test readlink - // - // Increase size of buffer to readlink untile whole symlink body will be read. - // Check readlink result on every iteration. - - while (1) { - memset(buffer, 0, size + 1); - num = readlink(new_path, buffer, size); - if (num < 1 || num > size) { - perror("checklink: readlink failed"); - free(buffer); - return 1; - } - // Make sure that readlink did not break things - if (buffer[num] != 0) { - printf - ("checklink : readlink corrupts memory\n"); - free(buffer); - return 1; - } - // Whole expected symlink body is read - if (num < size) - break; - - // Only part of symlink body was read. So we make a bigger buffer - // and call `readlink' again. - size *= 2; - if ((buffer = realloc(buffer, size + 1)) == NULL) { - perror("checklink: realloc failed"); - return 1; - } - } - } - free(buffer); - free(line_buffer); - return 0; -} diff --git a/testcases/kernel/fs/mongo/reiser_fract_tree.c b/testcases/kernel/fs/mongo/reiser_fract_tree.c deleted file mode 100755 index af2fa46a..00000000 --- a/testcases/kernel/fs/mongo/reiser_fract_tree.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -char tdir[256]; -char path[256]; -long stats = 0; - -void print_usage() -{ - printf(" -This program creates files in a tree of random depth and branching. Files vary -in size randomly according to a distribution function which seems to model real -file systems. This distribution function has a median size of median_file_size -(Median file size is hypothesized to be proportional to the average per file -space wastage. Notice how that implies that with a more efficient file system -file size usage patterns will in the long term move to a lower median file -size), and a maximum size of max_file_size. Directories vary in size according -to the same distribution function but with separate parameters to control median -and maximum size for the number of files within them, and the number of -subdirectories within them. This program prunes some empty subdirectories in a -way that causes parents of leaf directories to branch less than -median_dir_branching. - - To avoid having one large file distort the results such that you have -to benchmark many times set max_file_size to not more than -bytes_to_consume/10. If maximum/median is a small integer, then -randomness is very poor. This is a bug, Nikita, please find some -clever way to fix it. If it is 0, then the program crashes.... - -For isolating performance consequences of design variations on -particular file or directory size ranges, try setting their median size and -max_size to both equal the max size of the file size range you want -to test. - -To avoid having one large file distort the results set max_file_size to -not more than bytes_to_consume/10. Using a distribution function for -the sizes of writes would be a natural next step in developing this program.\n\n"); - - printf - ("Usage: reiser_fract_tree bytes_to_consume median_file_size max_file_size median_dir_nr_files max_directory_nr_files median_dir_branching max_dir_branching write_buffer_size /testfs_mount_point print_stats_flag\n\n"); -} - -/* #define DEBUG */ - -char *write_buffer; /* buffer from which we write */ -int write_buffer_size = 0; /* gets reset to an argv */ -static int already_whined = 0; /* keep out of disk space errors from being - endless by tracking whether we already - printed the message */ -long bytes_to_consume = 0; /* create files until their total number of - bytes exceeds this number, but not by more - than 1/10th */ -long byte_total = 0; /* bytes created so far */ - -/* statistics on sizes of files we attempted to create */ -int fsz_0_100 = 0; -int fsz_100_1k = 0; -int fsz_1k_10k = 0; -int fsz_10k_100k = 0; -int fsz_100k_1m = 0; -int fsz_1m_10m = 0; -int fsz_10m_larger = 0; - -void chngdir(char *name) -{ - int i; - - if (name[0] == '.' && name[1] == '.') { - for (i = strlen(path); i > 0; i--) { - if (path[i] == '/') { - path[i] = 0; - break; - } - } - } else { - strcat(path, "/"); - strcat(path, name); - } -} - -/* this is the core statistical distribution function, and it is used for file - sizes, directory sizes, etc. */ -int determine_size(double median_size, - double max_size /* The maximal value of size */ ) -{ - /* when x is half of its random range (max_size/median_size), result is - median_size */ - int nr_random, granularity_reducer; - double size, double_nr_random; - - /* it is a feature for us that this repeats identically every time it is run, - as otherwise meaningless variances would affect our results and require us - to use a higher number of benchmarks to achieve low noise results. */ - nr_random = rand(); - median_size++; /* avoids divide by zero errors */ - - /* this code does poorly when max_size is not a lot more than median size, - and that needs fixing */ - - /* THE NEXT 2 LINES ARE THE HEART OF THE PROGRAM */ - - /* keep x below the value that when multiplied by median size on the next - line will equal max_size */ - /* the granularity_reducer is to handle the case where max_size is near - median_size, since '%' can only take ints, we need this complicated what - of handling that for small values of max_size/median_size by making - large ints out of small ints temporarily. */ - if (max_size / median_size < 1024) - granularity_reducer = 1024 * 1024; - else - granularity_reducer = 1; - nr_random = - nr_random % - ((int) - (granularity_reducer * - (((double)max_size) / ((double)median_size)))); - double_nr_random = ((double)nr_random) / (granularity_reducer); - size = - median_size * (1 / - (1 - - (double_nr_random) / (((double)max_size) / - ((double)median_size))) - 1); - return ((int)size); -} - -/* generate a unique filename */ -void get_name_by_number(long this_files_number, char *str) -{ - sprintf(str, "%lu", this_files_number); -} - -/* make a file of a specified size */ -void make_file(int size) -{ - char string[128] = { 0 }; - char *str = string; - char fname[256]; - int fd = 0; - int error; - static long this_files_number = 1; - - /* collect statistics about the size of files created, or more precisely, the - size of files that we will attempt to create. */ - if (size <= 100) - fsz_0_100++; - else if (size <= 1000) - fsz_100_1k++; - else if (size <= 10 * 1000) - fsz_1k_10k++; - else if (size <= 100 * 1000) - fsz_10k_100k++; - else if (size <= 1000 * 1000) - fsz_100k_1m++; - else if (size <= 10 * 1000 * 1000) - fsz_1m_10m++; - else - fsz_10m_larger++; - - /* construct a name for the file */ - get_name_by_number(this_files_number++, str); - strcpy(fname, path); - strcat(fname, "/"); - strcat(fname, str); - - /* open the file, and deal with the various errors that can occur */ - - if ((fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0777)) == -1) { - if (errno == ENOSPC) { - if (!already_whined) { - printf - ("reiser-2021A: out of disk (or inodes) space, will keep trying\n"); - already_whined = 1; /* we continue other file creation in out of - space conditions */ - } - return; - } - /* it is sometimes useful to be able to run this program more than once - inside the same directory, and that means skipping over filenames that - already exist. Thus we ignore EEXIST, and pay attention to all - else. */ - if (errno == EEXIST) { /* just skip existing file */ - return; - } - perror("open"); - exit(errno); - } - /* write to the file until it is the right size, handling the various error - conditions appropriately */ - - while (size > 0) { - size -= (error = - write(fd, write_buffer, - (size < - write_buffer_size - - 1) ? size : (write_buffer_size - 1))); - if (error == -1) { - if (errno == ENOSPC) { - if (!already_whined) { - printf - ("reiser-2022: out of disk space, will keep trying\n"); - already_whined = 1; - } - close(fd); - return; - } - perror("write() failed"); - exit(errno); - } - } - - /* close the file */ - if (close(fd)) { - perror("close() failed"); - exit(errno); - } -} - -/* print the statistics on how many files were created of what size */ - -void print_stats() -{ - if (!stats) - return; - - printf("\n"); - printf("File stats: Units are decimal (1k = 1000)\n"); - printf("files 0-100 : %i\n", fsz_0_100); - printf("files 100-1K : %i\n", fsz_100_1k); - printf("files 1K-10K : %i\n", fsz_1k_10k); - printf("files 10K-100K : %i\n", fsz_10k_100k); - printf("files 100K-1M : %i\n", fsz_100k_1m); - printf("files 1M-10M : %i\n", fsz_1m_10m); - printf("files 10M-larger : %i\n", fsz_10m_larger); - printf("total bytes written : %lu\n", byte_total); - -} - -/* predict the number of files that will be created before max_bytes total - length of files is reached */ -long determine_nr_of_files(int median_file_size, double max_file_size, - long bytes_to_consume) -{ - long nr_of_files = 0, byte_total = 0; - - /* the next line is not necessary as 1 is the default, it is just cautious - coding */ - srand(1); - while (byte_total < bytes_to_consume) { - byte_total += determine_size(median_file_size, max_file_size); - nr_of_files++; - } - /* reset the random number generator so that when we determine_size() of the - files later they will be created with the same "random" sequence used in - this calculation */ - srand(1); -#ifdef DEBUG - printf("number of files is %d\n", (int)nr_of_files); -#endif /* DEBUG */ - fflush(NULL); - return nr_of_files; -} - -/* fill the current working directory with nr_files_this_directory number of - files*/ - -void fill_this_directory(long nr_files_this_directory, long median_file_size, - long maximum_size) -{ - long size; - -#ifdef DEBUG - printf("filling with %lu files, ", nr_files_this_directory); -#endif - while (nr_files_this_directory--) { - size = determine_size(median_file_size, maximum_size); - byte_total += size; - make_file(size); - } -} - -/* this will unfortunately handle out of disk space by forever trying */ -/* What we should do in out of space situaltion ? I think we must skip this - directory and continue files/dirs creation process. Error value (!= 0) - indicates that we can't go to this directory. -zam */ -int make_directory(char *dirname) -{ - static long this_directory_number = 0; - - strcpy(tdir, path); - strcat(tdir, "/"); - strcat(tdir, dirname); - - if (mkdir(tdir, 0755) == -1) { - if (errno == ENOSPC) { - if (!already_whined) { - printf("reiser-2021: out of disk space, "); - already_whined = 1; - } - return errno; - } - /* it is sometimes useful to be able to run this program more than once - inside the same directory, and that means skipping over filenames that - already exist. Thus we ignore EEXIST, and pay attention to all else. */ - if (errno != EEXIST) { - perror("mkdir"); - exit(errno); - } - } - sprintf(dirname, "d%lu", this_directory_number++); - strcpy(tdir, path); - strcat(tdir, "/"); - strcat(tdir, dirname); - - return 0; -} - -/* assumes we are already chdir'd into a directory that the subtree is rooted - at. Fills the directory with files and subdirectories, cd's into those - subdirectories, and recurses upon itself */ - -void do_subtree( - /* the start and end of the portion of the directory sizes - array which corresponds to the sizes of the directories - composing this subtree */ - /* sizes_end minus sizes_start is equal to the number of - directories in this subtree */ - long *sizes_start, long *sizes_end, - long median_file_size, long maximum_file_size, - long median_dir_branching, long max_dir_branching) -{ - long *p; - long *sub_start; - long *sub_end; - int index_subdirectory_to_add_directory_to; - long *dirs_in_subtrees; - char *subtree_name; - long *sizes_index = sizes_start; - char subtree_name_array[128]; - long this_directory_branching; - static long this_directorys_number; - - subtree_name = subtree_name_array; - /* fill this directory with its number of files */ - fill_this_directory(*sizes_index, median_file_size, maximum_file_size); - sizes_index++; - /* ok, now randomly assign directories (and their number of files) among the - subdirectories that will be created if at least one directory is assigned - to it */ - - /* this will cause the random number sequence to not match the one used in - determine_nr_files() I need to accumulate my values in an array - beforehand. I'll code that later. */ - /* worry about whether 0 or 1 is a problem value */ - this_directory_branching = - determine_size(median_dir_branching, max_dir_branching) + 1; - - /* create an array holding the number of directories assigned to each - potential subdirectory */ - dirs_in_subtrees = calloc(this_directory_branching, sizeof(long)); - while (sizes_index <= sizes_end) { - index_subdirectory_to_add_directory_to = - (rand() % this_directory_branching); - (* - (dirs_in_subtrees + index_subdirectory_to_add_directory_to))++; - sizes_index++; - } - /* the +1 is for the fill_directory() we did above */ - sizes_index = sizes_start + 1; - - /* go through each potential subdirectory, and if at least one directory has - been assigned to it, create it and recurse */ - for (p = dirs_in_subtrees; - p < (dirs_in_subtrees + this_directory_branching); p++) { - if (*p) { - int nocd; - sprintf(subtree_name, "d%lu", this_directorys_number++); - nocd = make_directory(subtree_name); - /* if make_dir.. may fails (in out of space situation), we continue - creation process in same dir */ - if (!nocd) - chngdir(subtree_name); - sub_start = sizes_index; - /* the minus one is because *p is the number of elements and arrays start at 0 */ - sub_end = (sizes_index + (*p - 1)); - -#ifdef DEBUG - /* comment this back in if the array logic has you going cross-eyed */ - /* printf ("sizes_start is %p, sizes_index is %p, sizes_index+p is %p, sizes_end is %p\n", sizes_start, sub_start, sub_end, sizes_end); */ -#endif - do_subtree(sub_start, sub_end, median_file_size, - maximum_file_size, median_dir_branching, - max_dir_branching); - if (!nocd) - chngdir(".."); - } - sizes_index += *p; - } -} - -/* We have already determined that nr_files can fit in bytes_to_consume space. - Fill the sizes array with the number of files to be in each directory, and - then call do_subtree to fill the tree with files and directories. */ - -void make_fractal_tree(long median_file_size, long maximum_file_size, - long median_dir_nr_files, long max_dir_nr_files, - long median_dir_branching, long max_dir_branching, - long nr_files) -{ - long *sizes_start; - long *sizes_end; - long *sizes_index; - long remaining_files = nr_files; - - /* collect together array of directory sizes for whole filesystem. This - cannot easily be done recursively without distorting the directory sizes - and making deeper directories smaller. Send me the code if you - disagree.:-) */ - /* we almost certainly don't need this much space, but so what.... */ - sizes_index = sizes_start = malloc(nr_files * sizeof(long)); - for (; remaining_files > 0;) { - *sizes_index = - determine_size(median_dir_nr_files, max_dir_nr_files); - // we alloc space for nr_files, so we should avoid - // number of files in directory = 0 -grev. - if (*sizes_index == 0) - *sizes_index = 1; - *sizes_index = - (*sizes_index < - remaining_files) ? *sizes_index : remaining_files; - -#ifdef DEBUG - printf("*sizes_index == %lu, ", *sizes_index); -#endif - remaining_files -= *sizes_index; - sizes_index++; - } - /* don't decrement below sizes_start if nr_files is 0 */ - sizes_end = (sizes_index-- > sizes_start) ? sizes_index : sizes_start; - - sizes_index = sizes_start; - srand(1); - do_subtree(sizes_start, sizes_end, median_file_size, maximum_file_size, - median_dir_branching, max_dir_branching); - -} - -int main(int argc, char *argv[]) -{ - /* initialized from argv[] */ - long median_file_size, - median_dir_branching, - median_dir_nr_files, - max_dir_nr_files, max_dir_branching, max_file_size; - long nr_of_files = 0; /* files to be created */ - - if (argc != 11) { - print_usage(); - exit(1); - } - - write_buffer_size = atoi(argv[8]); - write_buffer = malloc(write_buffer_size); - memset(write_buffer, 'a', write_buffer_size); - - /* the number of bytes that we desire this tree to consume. It will actually - consume more, because the last file will overshoot by a random amount, and - because the directories and metadata will consume space. */ - bytes_to_consume = atol(argv[1]); - max_file_size = atol(argv[3]); - median_file_size = atol(argv[2]); - /* Figure out how many random files will fit into bytes_to_consume bytes. We - depend on resetting rand() to get the same result later. */ - nr_of_files = - determine_nr_of_files(median_file_size, max_file_size, - bytes_to_consume); - - strcpy(path, argv[9]); - mkdir(path, 0755); - stats = atol(argv[10]); - median_dir_branching = atol(argv[6]); - max_dir_branching = atol(argv[7]); - median_dir_nr_files = atol(argv[4]); - max_dir_nr_files = atol(argv[5]); - make_fractal_tree(median_file_size, max_file_size, median_dir_nr_files, - max_dir_nr_files, median_dir_branching, - max_dir_branching, nr_of_files); - print_stats(); - if (stats) - printf("\nreiser_fract_tree finished\n"); - - return 0; -} diff --git a/testcases/kernel/fs/mongo/run_mongo b/testcases/kernel/fs/mongo/run_mongo deleted file mode 100755 index e22e2b9a..00000000 --- a/testcases/kernel/fs/mongo/run_mongo +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# -# Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README -# -if [ $# -lt 2 ] -then - echo - echo "Usage : run_mogo " - echo - echo "Example :" - echo "# run_mogo /dev/hdx1 2" - echo - exit -fi - -DEVICE=$1 -NPROC=$2 - -y="Yes" -echo "WARNING : All data will be erased on device=$DEVICE " -echo "Run ? (Yes | no)" -read x - -if [ -z $x ] -then - exit -fi - -if ! [ $x = $y ] -then - exit -fi - -./mongo.pl reiserfs $DEVICE /testfs reiserfs $NPROC -./mongo.pl ext2 $DEVICE /testfs ext2 $NPROC -./mongo_compare ./results/ext2.tbl ./results/reiserfs.tbl ./results/html/ext2_vs_reiserfs diff --git a/testcases/kernel/fs/mongo/summ.c b/testcases/kernel/fs/mongo/summ.c deleted file mode 100755 index c5712a4e..00000000 --- a/testcases/kernel/fs/mongo/summ.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README - */ - -#include -#include -char str[100]; - -int main(int argc, char **argv) -{ - char c, *p; - int sum = 0, n = 0; - - p = str; - while ((c = getchar()) != EOF) { - if (c != '\n') { - *p++ = c; - } else { - *p = '\0'; - n = atol(str); - sum += n; - printf("%i\n", sum); - p = str; - *p = '\0'; - } - } -} diff --git a/testcases/kernel/fs/mongo/test.sh b/testcases/kernel/fs/mongo/test.sh deleted file mode 100755 index 5ec4e313..00000000 --- a/testcases/kernel/fs/mongo/test.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/sh -#To exectute this you need mongo filesystem utility. -#Run this inside the mongo directory. -#mongo utility can be found in www.namesys.com/benchmarks/mongo-xxx.tgz -#Description-this script tests the mongo utility which actulally give the time ie cpu time -#Real time etc on reiserfile system and jfs filesystem. -#created by prakash.banu@wipro.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 -# - -LOG_DIR=$PWD -TEST_DIR=testdir - - - #should be root to execute this script . - if [ $(id -ru) -ne 0 ]; then - echo "This script must be run as root" - exit - fi - #set the PATH variable if its not done . -export PATH=$PATH:/sbin -lsmod |grep reiserfs - - if [ $? -ne 0 ]; then - echo "inserting reiserfs and its dependencies" - fi -modprobe reiserfs - if [ $? -ne 0 ]; then - echo "check wheather reiserfs is been compiled in the kernel" - fi - -lsmod |grep loop - if [ $? -ne 0 ]; then - echo "inserting loopback device module" - fi -modprobe loop - if [ $? -ne 0 ]; then - echo "check wheather loopback device option is been compiled in the kernel" - fi - - #run the mongo test on reiserfs file system type -reiserfs() -{ -cat > fs.sh < /dev/null 2>&1 -losetup /dev/loop0 reiserfs -mkdir -p $TEST_DIR -./mongo.pl LOG=/tmp/logfile1 file_size=10000 bytes=100000 fstype=reiserfs dev=/dev/loop0 dir=$TEST_DIR RUN log=$LOG_DIR/reiserlog > /dev/null 2>&1 - -echo "RESULTS LOGGED IN $LOG_DIR/reiserlog" -export PATH=$PATH:/sbin -losetup -d /dev/loop0 - -EOF -} - - -#To run on jfs file system type -JFS() -{ -cat >> fs.sh < /dev/null 2>&1 -losetup /dev/loop0 jfs -./mongo.pl LOG=/tmp/logfile1 file_size=10000 bytes=100000 fstype=jfs dev=/dev/loop0 dir=$TEST_DIR RUN log=$LOG_DIR/jfslog - -echo "RESULTS LOGGED IN $LOG_DIR/jfslog" -export PATH=$PATH:/sbin -losetup -d /dev/loop0 -echo "rm -rf ./fs.sh" >> ./fs.sh 2>&1 -EOF -} - - -echo -ne "TEST ON REISERFS?[y/N]:" -read ker - -case $ker in -y|Y) reiserfs -esac - -echo -ne "TEST ON JFS[y/N]: " -read ker - -case $ker in -y|Y) JFS -esac - -echo "THIS MAY TAKE SOME MINUTES" -sh fs.sh - -#performing cleanup -#losetup -d /dev/loop0 -rm -rf $TEST_DIR diff --git a/testcases/kernel/fs/quota_remount/quota_remount_test01.sh b/testcases/kernel/fs/quota_remount/quota_remount_test01.sh index 25d9f8fc..3d087ab3 100755 --- a/testcases/kernel/fs/quota_remount/quota_remount_test01.sh +++ b/testcases/kernel/fs/quota_remount/quota_remount_test01.sh @@ -12,7 +12,6 @@ TST_NEEDS_TMPDIR=1 TST_SETUP=do_setup TST_CLEANUP=do_clean TST_TESTFUNC=do_test -TST_MIN_KVER="2.6.26" do_setup() { @@ -67,7 +66,8 @@ do_test() newblocks=$(get_blocks) if [ $blocks -eq $newblocks ]; then - tst_brk TFAIL "usage did not change after remount" + tst_res TFAIL "usage did not change after remount" + return fi tst_res TPASS "quota on remount passed" diff --git a/testcases/kernel/fs/read_all/read_all.c b/testcases/kernel/fs/read_all/read_all.c index 266678ea..e18945a3 100755 --- a/testcases/kernel/fs/read_all/read_all.c +++ b/testcases/kernel/fs/read_all/read_all.c @@ -57,7 +57,7 @@ struct queue { sem_t sem; - int front; + tst_atomic_t front; int back; char data[QUEUE_SIZE]; char popped[BUFFER_SIZE]; @@ -67,7 +67,7 @@ struct worker { int i; pid_t pid; struct queue *q; - int last_seen; + tst_atomic_t last_seen; unsigned int kill_sent:1; }; @@ -94,11 +94,17 @@ static int worker_timeout; static int timeout_warnings_left = 15; static char *blacklist[] = { - NULL, /* reserved for -e parameter */ + "/reserved/", /* reserved for -e parameter */ "/sys/kernel/debug/*", "/sys/devices/platform/*/eeprom", "/sys/devices/platform/*/nvmem", "/sys/*/cpu??*(?)/*", /* cpu* entries with 2 or more digits */ + NULL +}; + +static char *ratelimit_list[] = { + "/sys/devices/*/tpm*", + NULL, }; static long long epoch; @@ -193,19 +199,43 @@ static void sanitize_str(char *buf, ssize_t count) strcpy(buf + MAX_DISPLAY, "..."); } -static int is_blacklisted(const char *path) +static int is_onlist(const char *path, char *list[]) { - unsigned int i; + unsigned int i = 0; - for (i = 0; i < ARRAY_SIZE(blacklist); i++) { - if (blacklist[i] && !fnmatch(blacklist[i], path, FNM_EXTMATCH)) { - if (verbose) - tst_res(TINFO, "Ignoring %s", path); + while (1) { + const char *pattern = list[i++]; + + if (!pattern) + break; + if (!fnmatch(pattern, path, FNM_EXTMATCH)) return 1; - } } return 0; + +} + +static int is_blacklisted(const char *path) +{ + int ret; + + ret = is_onlist(path, blacklist); + if (ret && verbose) + tst_res(TINFO, "Ignoring %s", path); + + return ret; +} + +static int is_ratelimitted(const char *path) +{ + int ret; + + ret = is_onlist(path, ratelimit_list); + if (ret && verbose) + tst_res(TINFO, "Limiting to single worker %s", path); + + return ret; } static void worker_heartbeat(const int worker) @@ -503,6 +533,9 @@ static int sched_work(const int first_worker, int min_ttl = worker_timeout, sleep_time = 1; int pushed, workers_pushed = 0; + if (is_ratelimitted(path)) + repetitions = 1; + for (i = 0, j = first_worker; i < repetitions; j++) { if (j >= worker_count) j = 0; @@ -565,7 +598,7 @@ static void setup(void) worker_timeout); } else { worker_timeout = 10 * tst_remaining_runtime(); - tst_res(TINFO, "Worker timeout set to 10%% of max_runtime: %dms", + tst_res(TINFO, "Worker timeout set to 10%% of runtime: %dms", worker_timeout); } worker_timeout *= 1000; @@ -713,5 +746,5 @@ static struct tst_test test = { .cleanup = cleanup, .test_all = run, .forks_child = 1, - .max_runtime = 100, + .runtime = 100, }; diff --git a/testcases/kernel/fs/scsi/ltpfs/Ltpfs.h b/testcases/kernel/fs/scsi/ltpfs/Ltpfs.h deleted file mode 100755 index 24a85c95..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/Ltpfs.h +++ /dev/null @@ -1,71 +0,0 @@ - -#define FS_LTP_TEST_COMPONENT 0x00020999 -#define FS_LTP_TEST_CLASS "ltp_test" -#define FS_LTP_TEST_HID "FS0999" -#define FS_LTP_TEST_DRIVER_NAME "FS LTP Test Driver" -#define FS_LTP_TEST_DEVICE_NAME "LTP Test" -#define FS_LTP_TEST_FILE_STATE "state" -#define FS_LTP_TEST_NOTIFY_STATUS 0x80 -#define FS_LTP_TEST_STATUS_OFFLINE 0x00 -#define FS_LTP_TEST_STATUS_ONLINE 0x01 -#define FS_LTP_TEST_STATUS_UNKNOWN 0xFF -#define _COMPONENT FS_LTP_TEST_COMPONENT -#define FS_TLP_TEST_MODULE_NAME ("fs_ltp_test") -#define FS_NS_SYSTEM_BUS "_SB_" -#define FS_BATTERY_FORMAT_BIF "NNNNNNNNNSSSS" -#define FS_BATTERY_FORMAT_BST "NNNN" - - -#define FS_TYPE_ANY 0x00 -#define FS_TYPE_INTEGER 0x01 /* Byte/Word/Dword/Zero/One/Ones */ -#define FS_TYPE_STRING 0x02 -#define FS_TYPE_BUFFER 0x03 -#define FS_TYPE_PACKAGE 0x04 /* byte_const, multiple data_term/Constant/super_name */ -#define FS_TYPE_FIELD_UNIT 0x05 -#define FS_TYPE_DEVICE 0x06 /* Name, multiple Node */ -#define FS_TYPE_EVENT 0x07 -#define FS_TYPE_METHOD 0x08 /* Name, byte_const, multiple Code */ -#define FS_TYPE_MUTEX 0x09 -#define FS_TYPE_REGION 0x0A -#define FS_TYPE_POWER 0x0B /* Name,byte_const,word_const,multi Node */ -#define FS_TYPE_PROCESSOR 0x0C /* Name,byte_const,Dword_const,byte_const,multi nm_o */ -#define FS_TYPE_THERMAL 0x0D /* Name, multiple Node */ -#define FS_TYPE_BUFFER_FIELD 0x0E -#define FS_TYPE_DDB_HANDLE 0x0F -#define FS_TYPE_DEBUG_OBJECT 0x10 - -#define FS_TYPE_EXTERNAL_MAX 0x10 -#define LTPMAJOR 256 - -/* Use 'k' as magic number */ -#define LTPFS_IOC_MAGIC 'k' -#define TOMINOR(x) ((x & 3) | ((x & 4) << 5)) - - -#define DEV_PATH "/dev" -#define LTP_FS_DIR_NAME "" -#define LTP_FS_DEV_NAME "LTPFS" -#define LTP_FS_DEV_NODE_PATH DEV_PATH "/" -#define LTP_FS_DEVICE_NAME DEV_PATH "/" LTP_FS_DEV_NAME -#define MINOR_SHIFT_BITS 3 -#define MAX_PARTITIONS 8 /* partition 0 + 7 more possible due to 3 bit partition number field */ -#define MAX_NUM_DISKS 3 /* number of real devices */ - -#define MPDEV_FLAG_CLEAR 0 -#define MPDEV_FLAG_SET 1 - -typedef struct _ltpdev_cmd { - u_int32_t cmd; // input - 0==recover, 1==fail - u_int32_t status; // ouput - 0==success -} ltpdev_cmd_t; - -typedef enum ltpdev_ioctl_cmds_s { - /* version commands */ - LTP_AIO_IOCTL_NUMBER = 0x5500, - LTP_BIO_IOCTL_NUMBER = 0x5501 -} ltpdev_ioctl_cmds_t; - -// define the ioctl cmds -#define LTPAIODEV_CMD _IOR( LTPMAJOR, LTP_AIO_IOCTL_NUMBER, ltpdev_cmd_t **) -#define LTPBIODEV_CMD _IOR( LTPMAJOR, LTP_BIO_IOCTL_NUMBER, ltpdev_cmd_t **) - diff --git a/testcases/kernel/fs/scsi/ltpfs/LtpfsCmds.c b/testcases/kernel/fs/scsi/ltpfs/LtpfsCmds.c deleted file mode 100755 index 0b561702..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/LtpfsCmds.c +++ /dev/null @@ -1,315 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) -#define IS_POSIX(fl) (fl->fl_flags & FL_POSIX) -#define TEST_MEM_SIZE 4096 -#define FALSE 0 -#include "Ltpfs.h" - -static int ltpdev_open(struct inode *inode, struct file *pfile); -static int ltpdev_release(struct inode *inode, struct file *pfile); -static int ltpdev_ioctl(struct inode *pinode, struct file *pfile, - unsigned int cmd, unsigned long arg); -static int do_buffer_c_tests(void); - -static struct block_device_operations blkops = { -open: ltpdev_open, -release:ltpdev_release, -ioctl: ltpdev_ioctl, -}; - -int ltp_fs_major = LTPMAJOR; -int test_iteration = 0; - -static char genhd_flags = 0; -static struct gendisk *gd_ptr; -static spinlock_t bdev_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; - -MODULE_AUTHOR("Martin Ridgeway "); -MODULE_DESCRIPTION(FS_LTP_TEST_DRIVER_NAME); -MODULE_LICENSE("GPL"); - -/* - * Device operations for the virtual FS devices - */ - -static struct pm_dev *ltp_pm_dev = NULL; -struct block_device *ltplookup_bdev(const char *path); -int path_lookup(const char *name, unsigned int flags, struct nameidata *nd); -//static int __emul_lookup_dentry(const char *name, struct nameidata *nd); -void path_release(struct nameidata *nd); - -static int ltpdev_open(struct inode *pinode, struct file *pfile) -{ - printk(KERN_ALERT "ltpdev_open \n"); - return 0; -} - -static int ltpdev_release(struct inode *pinode, struct file *pfile) -{ - - printk(KERN_ALERT "ltpdev_release \n"); - return 0; -} - -static int ltpdev_ioctl(struct inode *pinode, struct file *pfile, - unsigned int cmd, unsigned long arg) -{ - - struct bio *my_bio = NULL; - struct bio *my_bio_copy = NULL; - request_queue_t *q = NULL; - struct block_device *bdev = NULL; - unsigned long uaddr; - - unsigned int bytes_done = 100; - - int error = 0; - int rc = 0; - - /*****************************************************************************/ - - printk(KERN_ALERT "ltpdev_ioctl fs tests\n"); - - switch (cmd) { - - case LTPAIODEV_CMD: - printk(KERN_ALERT "Running AIO FS tests \n"); - printk(KERN_ALERT "AIO FS tests complete\n"); - break; - - case LTPBIODEV_CMD: - - printk(KERN_ALERT "Running BIO FS tests \n"); - - my_bio = bio_alloc(GFP_KERNEL, 0); - if (!my_bio) { - printk(KERN_ALERT - "Error getting kernel slab memory !!\n"); - } else { - printk(KERN_ALERT "kernel slab memory alloc OK\n"); - } - - bio_endio(my_bio, bytes_done, error); - - printk(KERN_ALERT "Return from bio_endio = %d \n", error); - - my_bio_copy = bio_clone(my_bio, GFP_ATOMIC); - - if (!my_bio_copy) { - printk(KERN_ALERT - "Error getting kernel bio clone !!\n"); - } else { - printk(KERN_ALERT "kernel bio clone OK\n"); - } - - my_bio_copy = bio_clone(my_bio, GFP_NOIO); - - if (!my_bio_copy) { - printk(KERN_ALERT - "Error getting kernel bio clone !!\n"); - } else { - printk(KERN_ALERT "kernel bio clone OK\n"); - } - -// q = bdev_get_queue(my_bio->bi_bdev); - -// rc = bio_phys_segments(q, my_bio); - -// rc = bio_hw_segments(q, my_bio); - - bdev = lookup_bdev(LTP_FS_DEVICE_NAME); - - printk(KERN_ALERT "return from bdev size %d\n", - bdev->bd_block_size); - - printk(KERN_ALERT "Return from phys_segments = %d \n", rc); - -// Don't use this API, causes system to hang and corrupts FS -// bio_put(my_bio); - - (char *)uaddr = kmalloc(TEST_MEM_SIZE, GFP_KERNEL); - - my_bio_copy = bio_map_user(bdev, uaddr, TEST_MEM_SIZE, FALSE); - - printk(KERN_ALERT "Return from bio_map_user %p\n", my_bio_copy); - - do_buffer_c_tests(); - - printk(KERN_ALERT "BIO FS tests complete\n"); - - break; - } - - return 0; -} - -static int ltp_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - return 0; -} - -int init_module(void) -{ - int result; - - printk(KERN_ALERT "ltpdev_init_module \n"); - - ltp_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, ltp_pm_callback); - - result = register_blkdev(ltp_fs_major, LTP_FS_DEV_NAME); - - printk(KERN_ALERT "LTP FS: register_blkdev result=%d major %d\n", - result, ltp_fs_major); - - if (result < 0) { - printk(KERN_ALERT "LTP FS: can't get major %d\n", ltp_fs_major); - return result; - } - - gd_ptr = kmalloc(sizeof(struct gendisk *), GFP_KERNEL); - - if (!gd_ptr) { - printk(KERN_ALERT "ERROR getting memory !!!\n"); - return 0; - } - - gd_ptr = alloc_disk(1); - - printk(KERN_ALERT "gd_ptr after alloc = %p \n", gd_ptr); - - gd_ptr->major = ltp_fs_major; - gd_ptr->first_minor = 0; - gd_ptr->fops = &blkops; - gd_ptr->driverfs_dev = NULL; - gd_ptr->capacity = MAX_NUM_DISKS; - gd_ptr->flags = genhd_flags; - - sprintf(gd_ptr->disk_name, LTP_FS_DEV_NAME); - - add_disk(gd_ptr); - - return 0; -} - -void cleanup_module(void) -{ - - printk(KERN_ALERT "Exiting module and cleaning up \n"); - - pm_unregister(ltp_pm_dev); - - put_disk(gd_ptr); - - del_gendisk(gd_ptr); - - unregister_blkdev(ltp_fs_major, LTP_FS_DEV_NAME); - -} - -static int do_buffer_c_tests() -{ - int line_no = 0; - - printk(KERN_ALERT "Starting buffer.c coverage tests... \n"); - - __buffer_error("Test file", line_no); - - printk(KERN_ALERT "buffer.c coverage tests complete...\n"); - - return 0; -} - -/** - * lookup_bdev - lookup a struct block_device by name - * - * @path: special file representing the block device - * - * Get a reference to the blockdevice at @path in the current - * namespace if possible and return it. Return ERR_PTR(error) - * otherwise. - */ -struct block_device *lookup_bdev(const char *path) -{ - struct block_device *bdev; - struct inode *inode; - struct nameidata nd; - int error; - - if (!path || !*path) - return ERR_PTR(-EINVAL); - - error = path_lookup(path, LOOKUP_FOLLOW, &nd); - if (error) - return ERR_PTR(error); - - inode = nd.dentry->d_inode; - error = -ENOTBLK; - if (!S_ISBLK(inode->i_mode)) - goto fail; - error = -EACCES; - if (nd.mnt->mnt_flags & MNT_NODEV) - goto fail; - error = bd_acquire(inode); - if (error) - goto fail; - bdev = inode->i_bdev; - -out: - path_release(&nd); - return bdev; -fail: - bdev = ERR_PTR(error); - goto out; -} - -int bd_acquire(struct inode *inode) -{ - struct block_device *bdev; - spin_lock(&bdev_lock); - if (inode->i_bdev) { - atomic_inc(&inode->i_bdev->bd_count); - spin_unlock(&bdev_lock); - return 0; - } - spin_unlock(&bdev_lock); - bdev = bdget(kdev_t_to_nr(inode->i_rdev)); - if (!bdev) - return -ENOMEM; - spin_lock(&bdev_lock); - if (!inode->i_bdev) { - inode->i_bdev = bdev; - inode->i_mapping = bdev->bd_inode->i_mapping; - list_add(&inode->i_devices, &bdev->bd_inodes); - } else if (inode->i_bdev != bdev) - BUG(); - spin_unlock(&bdev_lock); - return 0; -} diff --git a/testcases/kernel/fs/scsi/ltpfs/Makefile b/testcases/kernel/fs/scsi/ltpfs/Makefile deleted file mode 100755 index 5f403066..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# -# Makefile for GCOV profiling kernel module -# - -#KERNELDIR := ../linux-2.5.73 -CFLAGS := $(CFLAGS) -Wall -g - -ifneq ($(KERNELRELEASE),) - -obj-m := LtpfsCmds.o -obj-p := ltpfstest -else -KDIR := /lib/modules/$(shell uname -r)/build -PWD := $(shell pwd) - -default: - $(MAKE) -C $(KDIR) M=$(PWD) modules - ${CC} $(CFLAGS) -o ltpfstest -lm main.c -# $(MAKE) -C $(KERNELDIR) M=$(PWD) modules -endif - -clean: - rm -f LtpfsCmds.o - rm -f LtpfsCmds.ko - rm -f LtpfsCmds.bb - rm -f LtpfsCmds.bbg - rm -f LtpfsCmds.mod.c - rm -f LtpfsCmds.mod.o - rm -f .*.mod* - rm -f .*.cmd diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part1 b/testcases/kernel/fs/scsi/ltpfs/ltpfs.part1 deleted file mode 100755 index 65086d53..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part1 +++ /dev/null @@ -1,4 +0,0 @@ -#DESCRIPTION:filesystem tests -# Check the EXT2 & NFS filesystems -bfs101 ltpfstest /test/growfiles/ext2 -bfs102 ltpfstest /test/growfiles/nfs diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part2 b/testcases/kernel/fs/scsi/ltpfs/ltpfs.part2 deleted file mode 100755 index 86514908..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part2 +++ /dev/null @@ -1,3 +0,0 @@ -#DESCRIPTION:filesystem tests -# Check the XFS filesystems -bfs201 ltpfstest /test/growfiles/xfs diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part3 b/testcases/kernel/fs/scsi/ltpfs/ltpfs.part3 deleted file mode 100755 index 4f2ee5f2..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part3 +++ /dev/null @@ -1,3 +0,0 @@ -#DESCRIPTION:filesystem tests -# Check the MSDOS filesystems -bfs101 ltpfstest /test/growfiles/msdos diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part4 b/testcases/kernel/fs/scsi/ltpfs/ltpfs.part4 deleted file mode 100755 index 84519512..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part4 +++ /dev/null @@ -1,3 +0,0 @@ -#DESCRIPTION:filesystem tests -# Check the Reiser filesystems -bfs401 ltpfstest /test/growfiles/reiser diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part5 b/testcases/kernel/fs/scsi/ltpfs/ltpfs.part5 deleted file mode 100755 index c3fa4d7a..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part5 +++ /dev/null @@ -1,3 +0,0 @@ -#DESCRIPTION:filesystem tests -# Check the Minix filesystems -bfs501 ltpfstest /test/growfiles/minix diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part6 b/testcases/kernel/fs/scsi/ltpfs/ltpfs.part6 deleted file mode 100755 index 0d600fe8..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part6 +++ /dev/null @@ -1,3 +0,0 @@ -#DESCRIPTION:filesystem tests -# Check the EXT3 filesystems -bfs601 ltpfstest /test/growfiles/ext3 diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part7 b/testcases/kernel/fs/scsi/ltpfs/ltpfs.part7 deleted file mode 100755 index c9904d6b..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfs.part7 +++ /dev/null @@ -1,3 +0,0 @@ -#DESCRIPTION:filesystem tests -# Check the JFS filesystems -bfs701 ltpfstest /test/growfiles/jfs diff --git a/testcases/kernel/fs/scsi/ltpfs/ltpfsio.sh b/testcases/kernel/fs/scsi/ltpfs/ltpfsio.sh deleted file mode 100755 index ed3d8e2a..00000000 --- a/testcases/kernel/fs/scsi/ltpfs/ltpfsio.sh +++ /dev/null @@ -1,154 +0,0 @@ --#!/bin/sh -# This script should be run prior to running executing the filesystem tests. -# valid devices need to be passed for the test to work correctly -# 10/06/03 mridge@us.ibm.com added instance and time command line options -# -# - -cd `dirname $0` -export LTPROOT=${PWD} -echo $LTPROOT | grep testscripts > /dev/null 2>&1 -if [ $? -eq 0 ]; then - cd .. - export LTPROOT=${PWD} -fi - -export TMPBASE="/tmp" - - -usage() -{ - cat <<-END >&2 - usage: ${0##*/} [ -a part1 ] [ -n nfsmount ] - defaults: - part1=$part1 - nfsmount=$nfsmount - ltproot=$TPROOT - tmpdir=$TMPBASE - - example: ${0##*/} -a hdc1 -b hdc2 -c hdc3 -d hdc4 -n mytesthost:/testmountdir - - - This test will ONLY run on a 2.5.66 or higher kernel system. - - - These operations are destructive so do NOT point the tests to partitions where the data shouldn't be overwritten. - Once these tests are started all data in the partitions you point to will be destroyed. - - END -exit -} - -while getopts :a:n:v: arg -do case $arg in - a) part1=$OPTARG;; - n) nfsmount=$OPTARG;; - v) verb=$OPTARG;; - - \?) echo "************** Help Info: ********************" - usage;; - esac -done - -if [ ! -n "$part1" ]; then - echo "Missing 1st partition. You must pass 4 partitions for testing" - usage; - exit -fi - -if [ ! -n "$nfsmount" ]; then - echo "Missing NFS partition. You must pass an NFS mount point for testing" - usage; - exit -fi - -export PATH="${PATH}:${LTPROOT}/testcases/bin" - - -mkdir /test >/dev/null 2>&1 -mkdir /test/growfiles >/dev/null 2>&1 -mkdir /test/growfiles/ext2 >/dev/null 2>&1 -mkdir /test/growfiles/msdos >/dev/null 2>&1 -mkdir /test/growfiles/reiser >/dev/null 2>&1 -mkdir /test/growfiles/minix >/dev/null 2>&1 -mkdir /test/growfiles/xfs >/dev/null 2>&1 -mkdir /test/growfiles/nfs >/dev/null 2>&1 -mkdir /test/growfiles/jfs >/dev/null 2>&1 -mkdir /test/growfiles/ext3 >/dev/null 2>&1 - - -mkfs -V -t ext2 /dev/$part1 - - -mount -v -t nfs $nfsmount /test/growfiles/nfs -mount -v /dev/$part1 /test/growfiles/ext2 - - -echo "************ Running tests " -sort -R ${LTPROOT}/runtest/ltpfs.part1 -o ${TMPBASE}/ltpfs.part1 - -${LTPROOT}/pan/pan -e -S -a ltpfspart1 -n ltpfspart1 -l lvmlogfile -f ${TMPBASE}/ltpfs.part1 & - -wait $! - -umount -v -t nfs $nfsmount -umount -v /dev/$part1 -mkfs.xfs -f /dev/$part1 -mount -v /dev/$part1 /test/growfiles/xfs - - -sort -R ${LTPROOT}/runtest/ltpfs.part2 -o ${TMPBASE}/ltpfs.part2 - -${LTPROOT}/pan/pan -e -S -a ltpfspart2 -n ltpfspart2 -l lvmlogfile -f ${TMPBASE}/ltpfs.part2 & - -wait $! - -mkfs -V -t msdos /dev/$part1 -umount -v /dev/$part1 -mount -v /dev/$part1 /test/growfiles/msdos - -sort -R ${LTPROOT}/runtest/ltpfs.part3 -o ${TMPBASE}/ltpfs.part3 - -${LTPROOT}/pan/pan -e -S -a ltpfspart3 -n ltpfspart3 -l lvmlogfile -f ${TMPBASE}/ltpfs.part3 & - -wait $! - -umount -v /dev/$part1 -mkreiserfs /dev/$part1 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Ltpfs.h" - -#define M_2PI (M_PI*2) -#define MAXN 4096 -#define MAXFSIZE 1024 * 192 -#define FILE_CREATE_COUNT 256 -#define FAIL 0 -#define SUCCESS 1 -#define MAXNUM 5000 -#define BUFFSIZE 8192 -#define AVEFSIZE (MAXFSIZE/2) -#define POOLDISKSPACE (AVEFSIZE*128) -#define MAXERROR 1024 -#define FILES_ONLY 0x01 -#define ALL 0x00 - -// Globals - -char wbuf[MAXFSIZE]; -int startc = 0; -int showchar[] = { 124, 47, 45, 92, 124, 47, 45, 92 }; - -int nullFileHandle; -static int openlog[2] = { 0, 0 }; - -int cFileCount, dFileCount, errorCount; -static int disk_space_pool = 0; -char rootPath[BUFFSIZE]; - -int LTP_fs_open_block_device(void); -int do_fs_thump_tests(char *path); -int do_create_file_test(char *path); -int makedir(char *dir1); -int changedir(char *dir); -int do_random_access_test(int maxNum); -int do_random_create_delete(int maxNum); -int create_file(char *filename); -int delete_file(char *filename); -int gen_random_file_size(int min, int max); -int open_read_close(char *fname); -int create_or_delete(char *fname); -int do_tree_cleanup(char *path, int flag); -int cleanup_files(char *file, struct stat *statBuff, int flag); -int cleanup_dirs(char *file, struct stat *statBuff, int flag); - -int ltp_block_dev_handle = 0; /* handle to LTP Test block device */ -int ltp_fileHandle = 0; -char *fileBuf; - -int main(int argc, char **argv) -{ - - ltpdev_cmd_t cmd = { 0, 0 }; - int rc, i, tmpHandle; - struct stat statBuf; - - printf("[%s] - Running test program\n", argv[0]); - - rc = LTP_fs_open_block_device(); - - if (!rc) { - - ltp_block_dev_handle = open(LTP_FS_DEVICE_NAME, O_RDWR); - - if (ltp_block_dev_handle < 0) { - printf - ("ERROR: Open of device %s failed %d errno = %d\n", - LTP_FS_DEVICE_NAME, ltp_block_dev_handle, errno); - } else { - rc = ioctl(ltp_block_dev_handle, LTPAIODEV_CMD, &cmd); - - printf("return from AIO ioctl %d \n", rc); - - rc = ioctl(ltp_block_dev_handle, LTPBIODEV_CMD, &cmd); - - printf("return from BIO ioctl %d \n", rc); - } - - } else { - printf("ERROR: Create/open block device failed\n"); - } - - ltp_fileHandle = - open("/tmp/testfile", O_CREAT | O_RDWR | O_SYNC | FASYNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - - if (ltp_fileHandle > 0) { - - tmpHandle = open("/usr/include/ctype.h", O_RDONLY); - - if (tmpHandle > 0) { - - rc = fstat(tmpHandle, &statBuf); - - if (!rc) { - fileBuf = malloc(statBuf.st_size); - - if (fileBuf) { - - read(tmpHandle, fileBuf, - statBuf.st_size); - close(tmpHandle); - write(ltp_fileHandle, fileBuf, - statBuf.st_size); - - for (i = 0; i < 100; i++) { - read(ltp_fileHandle, fileBuf, - statBuf.st_size * i); - write(ltp_fileHandle, fileBuf, - statBuf.st_size * i); - } - } - - } - - } else { - printf("ERROR: Create/open file failed\n"); - } - } - - printf("*** Starting FileSystem thump tests....****\n"); - printf("*** Please be patient, this may take a little while... ***\n"); - - for (i = 1; i < argc; i++) { - printf("Running test %d of %d on FileSystem %s \n", i, argc - 1, - argv[i]); - if (strcmp(argv[i], "|") != 0) { - strcpy(rootPath, argv[i]); - rc = do_fs_thump_tests(argv[i]); - if (rc != 0 && rc != ENOSPC) { - printf - ("ERROR: Failed on FileSystem %s with errno %d \n", - argv[i], rc); - } - } else { - printf("Test Program complete..\n"); - break; - } - - } - - printf("Test Program complete..\n"); - - return 0; -} - -int do_fs_thump_tests(char *path) -{ - int rc = 0; - - printf("Changing to directory %s \n", path); - - changedir(path); - - cFileCount = 0; - dFileCount = 0; - - rc |= do_create_file_test(path); - rc |= do_random_access_test(MAXNUM); - rc |= do_tree_cleanup(path, FILES_ONLY); - rc |= do_random_create_delete(MAXNUM); - rc |= do_tree_cleanup(path, ALL); - - return rc; - -} - -int do_tree_cleanup(char *path, int flag) -{ - - if (flag == FILES_ONLY) { - printf("Cleaning up test files...\n"); - ftw(path, (void *)cleanup_files, MAXNUM); - } else { - printf("Cleaning up everything in the test directory...\n"); - ftw(path, (void *)cleanup_files, MAXNUM); - ftw(path, (void *)cleanup_dirs, MAXNUM); - } - - return 0; -} - -int cleanup_files(char *file, struct stat *statBuff, int flag) -{ - int rc = 0; - - if (flag == FTW_F) { - if (unlink(file)) { - printf("ERROR:%d removing file %s\n", errno, file); - } - } - - return rc; -} - -int cleanup_dirs(char *file, struct stat *statBuff, int flag) -{ - int rc = 0; - - //printf("%s:Cleaning up directory %s \n", __FUNCTION__, file); - - if (strcmp(rootPath, file) == 0) { - return 0; - } - - if (flag == FTW_F) { - if (unlink(file)) { - printf("ERROR:%d removing file %s\n", errno, file); - } - } else if (flag == FTW_D) { - changedir(file); - ftw(file, (void *)cleanup_dirs, MAXNUM); - rmdir(file); - - } else { - printf("No idea what we found here\n"); - } - - return rc; -} - -int do_create_file_test(char *path) -{ - int i = 0; - int j = 0; - int k = 0; - int l = 0; - int rc = 0; - - char dir1[MAXN]; - char dir2[MAXN]; - char dir3[MAXN]; - char filename[MAXN]; - - time_t t; - - int maxfiles = 0xFFFFFF; - - time(&t); - - srandom((unsigned int)getpid() ^ - (((unsigned int)t << 16) | (unsigned int)t >> 16)); - - printf("Creating files...\n"); - - for (i = 0; i < FILE_CREATE_COUNT; i++) { - - sprintf(dir1, "%2.2x", i); - - makedir(dir1); - - changedir(dir1); - - for (j = 0; j < FILE_CREATE_COUNT; j++) { - - sprintf(dir2, "%2.2x", j); - - makedir(dir2); - - changedir(dir2); - - for (k = 0; k < FILE_CREATE_COUNT; k++) { - - sprintf(dir3, "%2.2x", k); - makedir(dir3); - changedir(dir3); - - for (l = 0; l < FILE_CREATE_COUNT; l++) { - sprintf(filename, "%s%s%s%2.2x", dir1, - dir2, dir3, l); - rc = create_file(filename); - if (rc != 0 || maxfiles < dFileCount++) { - if (rc != ENOSPC) { - printf - ("ERROR: failed error:%d creating all the test files ! \n", - errno); - printf - ("ERROR2: rc:%d -- dFileCount:%d \n", - rc, dFileCount); - } - goto end; - } - } - changedir("../"); - } - changedir("../"); - } - changedir("../"); - } -end: - fprintf(stderr, "\nTotal create files: %d\n", cFileCount); - printf("Done\n"); - return rc; -} - -int makedir(char *dir1) -{ - if (mkdir(dir1, S_IRWXU) < 0) { - perror(dir1); - return (errno); - } - return 0; -} - -int changedir(char *dir) -{ - if (chdir(dir) < 0) { - perror(dir); - return (errno); - } - - return 0; -} - -int create_file(char *filename) -{ - int fileHandle; - int randomsize; - - if ((fileHandle = creat(filename, S_IRWXU)) < 0) { - - fprintf(stderr, "\nERROR line %d: Total create files: %d\n", - __LINE__, cFileCount); - perror(filename); - return (errno); - } - - if ((randomsize = gen_random_file_size(0, MAXFSIZE)) < 0) { - randomsize = MAXFSIZE; - } - if (write(fileHandle, wbuf, randomsize) < 0) { - - fprintf(stderr, "\nERROR:%d line%d: Total create files: %d\n", - errno, __LINE__, cFileCount); - close(fileHandle); - - perror(filename); - return (errno); - } - - cFileCount++; - close(fileHandle); - return 0; -} - -int delete_file(char *filename) -{ - struct stat buf; - int st; - - st = stat(filename, &buf); - - if (st < 0) { - errorCount++; - printf("ERROR line %d: Getting file stats %s \n", __LINE__, - filename); - return (-1); - } - - disk_space_pool += buf.st_size; - - if (unlink(filename) < 0) { - errorCount++; - printf("ERROR line %d: Removing file %s \n", __LINE__, - filename); - return (-1); - } - - dFileCount++; - return 0; -} - -int LTP_fs_open_block_device() -{ - dev_t devt; - struct stat statbuf; - int rc = 0; - - if (ltp_block_dev_handle == 0) { - - /* check for the /dev/LTPFSTest subdir, and create if it does not exist. - * - * If devfs is running and mounted on /dev, these checks will all pass, - * so a new node will not be created. - */ - devt = makedev(LTPMAJOR, 0); - - rc = stat(LTP_FS_DEV_NODE_PATH, &statbuf); - - if (rc) { - if (errno == ENOENT) { - /* dev node does not exist. */ - rc = mkdir(LTP_FS_DEV_NODE_PATH, - (S_IFDIR | S_IRWXU | S_IRGRP | - S_IXGRP | S_IROTH | S_IXOTH)); - } else { - printf - ("ERROR: Problem with LTP FS dev directory. Error code from stat() is %d\n\n", - errno); - } - - } else { - if (!(statbuf.st_mode & S_IFDIR)) { - rc = unlink(LTP_FS_DEV_NODE_PATH); - if (!rc) { - rc = mkdir(LTP_FS_DEV_NODE_PATH, - (S_IFDIR | S_IRWXU | S_IRGRP - | S_IXGRP | S_IROTH | - S_IXOTH)); - } - } - } - - /* - * Check for the /dev/ltp-fs/block_device node, and create if it does not - * exist. - */ - rc = stat(LTP_FS_DEVICE_NAME, &statbuf); - if (rc) { - if (errno == ENOENT) { - /* dev node does not exist */ - rc = mknod(LTP_FS_DEVICE_NAME, - (S_IFBLK | S_IRUSR | S_IWUSR | - S_IRGRP | S_IWGRP), devt); - } else { - printf - ("ERROR:Problem with LTP FS block device node directory. Error code form stat() is %d\n\n", - errno); - } - - } else { - /* - * /dev/ltp-fs/block_device exists. Check to make sure it is for a - * block device and that it has the right major and minor. - */ - if ((!(statbuf.st_mode & S_IFBLK)) || - (statbuf.st_rdev != devt)) { - - /* Recreate the dev node. */ - rc = unlink(LTP_FS_DEVICE_NAME); - if (!rc) { - rc = mknod(LTP_FS_DEVICE_NAME, - (S_IFBLK | S_IRUSR | S_IWUSR - | S_IRGRP | S_IWGRP), devt); - } - } - } - - } - - return rc; -} - -int gen_random_file_size(int min, int max) -{ - double u1, u2, z; - int i; - int ave; - int range; - int ZZ; - if (min >= max) { - return (-1); - } - range = max - min; - ave = range / 2; - for (i = 0; i < 10; i++) { - u1 = ((double)(random() % 1000000)) / 1000000; - u2 = ((double)(random() % 1000000)) / 1000000; - z = sqrt(-2.0 * log(u1)) * cos(M_2PI * u2); - ZZ = min + (ave + (z * (ave / 4))); - if (ZZ >= min && ZZ < max) { - return (ZZ); - } - } - return (-1); -} - -int do_random_access_test(int maxNum) -{ - int r; - char fname[1024]; - time_t t; - int i; - - printf("Running random access test...\n"); - changedir(rootPath); - - if (maxNum < 1 || maxNum > MAXNUM) { - printf("out of size %d\n", maxNum); - return 1; - } - - time(&t); - srandom((unsigned int)getpid() ^ - (((unsigned int)t << 16) | (unsigned int)t >> 16)); - - if ((nullFileHandle = open("/dev/null", O_WRONLY)) < 0) { - perror("/dev/null"); - return (errno); - } - - /* 00/00/00/00 */ - for (i = 0; i < maxNum; i++) { - - r = random() % maxNum; - - sprintf(fname, "00/%2.2x/%2.2x/00%2.2x%2.2x%2.2x", - ((r >> 16) & 0xFF), - ((r >> 8) & 0xFF), - ((r >> 16) & 0xFF), ((r >> 8) & 0xFF), (r & 0xFF)); - - open_read_close(fname); - } - close(nullFileHandle); - printf("Success:\t%d\nFail:\t%d\n", openlog[SUCCESS], openlog[FAIL]); - return 0; -} - -int open_read_close(char *fname) -{ - int fileHandle, fileHandle2; - char buffer[BUFFSIZE]; - int c; - - if ((fileHandle = open(fname, O_RDONLY | O_SYNC | O_ASYNC)) < 0) { - openlog[FAIL]++; - printf("ERROR:opening file %s failed %d \n", fname, errno); - return (errno); - } - - if ((fileHandle2 = open(fname, O_RDONLY | O_SYNC | O_ASYNC)) < 0) { - openlog[FAIL]++; - printf("ERROR:2nd opening file %s failed %d \n", fname, errno); - return (errno); - } - - openlog[SUCCESS]++; - - while ((c = read(fileHandle, buffer, BUFFSIZE)) > 0) { - if (write(nullFileHandle, buffer, c) < 0) { - perror("/dev/null"); - printf("Opened\t %d\nUnopend:\t%d\n", openlog[SUCCESS], - openlog[FAIL]); - close(fileHandle2); - close(fileHandle); - return (errno); - } - if ((c = read(fileHandle2, buffer, BUFFSIZE)) > 0) { - if (write(nullFileHandle, buffer, c) < 0) { - perror("/dev/null"); - printf("Opened\t %d\nUnopend:\t%d\n", - openlog[SUCCESS], openlog[FAIL]); - close(fileHandle2); - close(fileHandle); - return (errno); - } - } - } - - if (c < 0) { - perror(fname); - printf("Opened\t %d\nUnopend:\t%d\n", openlog[SUCCESS], - openlog[FAIL]); - return (errno); - } - - close(fileHandle2); - close(fileHandle); - return 0; -} - -int create_or_delete(char *fname) -{ - int r, rc; - - r = (random() & 1); - - /* create */ - if ((create_file(fname) == 0)) { - rc = delete_file(fname); - } else { - printf("Error: %d creating random file \n", errno); - } - - if ((errorCount > dFileCount || errorCount > cFileCount) - && (errorCount > MAXERROR)) { - fprintf(stderr, "Too many errors -- Aborting test\n"); - fprintf(stderr, "Total create files: %d\n", cFileCount); - fprintf(stderr, "Total delete files: %d\n", dFileCount); - fprintf(stderr, "Total error : %d\n", errorCount); - return (MAXERROR); - } - - return 0; -} - -int do_random_create_delete(int maxNum) -{ - int r, rc = 0; - char fname[1024]; - time_t t; - int i; - - printf("Running random create/delete test...\n"); - - if (maxNum < 1 || maxNum > MAXNUM) { - printf("MAX out of size %d\n", maxNum); - return (maxNum); - } - - time(&t); - srandom((unsigned int)getpid() ^ - (((unsigned int)t << 16) | (unsigned int)t >> 16)); - - /* 00/00/00/00 */ - for (i = 0; i < maxNum && rc != MAXERROR; i++) { - r = random() % maxNum; - sprintf(fname, "00/%2.2x/%2.2x/00%2.2x%2.2x%2.2x", - ((r >> 16) & 0xFF), - ((r >> 8) & 0xFF), - ((r >> 16) & 0xFF), ((r >> 8) & 0xFF), (r & 0xFF)); - - rc = create_or_delete(fname); - } - - fprintf(stderr, "Total create files: %d\n", cFileCount); - fprintf(stderr, "Total delete files: %d\n", dFileCount); - fprintf(stderr, "Total error : %d\n", errorCount); - return (rc); -} diff --git a/testcases/kernel/fs/scsi/ltpscsi/Makefile b/testcases/kernel/fs/scsi/ltpscsi/Makefile deleted file mode 100755 index e404ce07..00000000 --- a/testcases/kernel/fs/scsi/ltpscsi/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -SHELL = /bin/sh - -EXECS = scsimain - -LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 - -CFLAGS = -g -O2 -Wall -D_REENTRANT $(LARGE_FILE_FLAGS) -# CFLAGS = -g -O2 -Wall -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS) -# CFLAGS = -g -O2 -Wall -pedantic -D_REENTRANT $(LARGE_FILE_FLAGS) - -LDFLAGS = - -all: $(EXECS) - -depend dep: - @set -e; for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \ - done > .depend - -clean: - /bin/rm -f *.o $(EXECS) core .depend - -scsimain: scsimain.o sg_err.o llseek.o - $(LD) -o $@ $(LDFLAGS) $^ -lpthread - -install: $(EXECS) - install -d $(INSTDIR) - @set -e; for name in $^; \ - do install -s -o root -g root -m 755 $$name $(INSTDIR); \ - done - install -d $(MANDIR)/$(MAN_PREF) - @set -e; for mp in $(MAN_PGS); \ - do install -o root -g root -m 644 $$mp $(MANDIR)/$(MAN_PREF); \ - gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \ - done - -uninstall: - dists="$(EXECS)"; \ - @set -e; for name in $$dists; do \ - rm -f $(INSTDIR)/$$name; \ - done - @set -e; for mp in $(MAN_PGS); do \ - rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \ - done - -ifeq (.depend,$(wildcard .depend)) -include .depend -endif diff --git a/testcases/kernel/fs/scsi/ltpscsi/llseek.c b/testcases/kernel/fs/scsi/ltpscsi/llseek.c deleted file mode 100755 index 25b77620..00000000 --- a/testcases/kernel/fs/scsi/ltpscsi/llseek.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * llseek.c -- stub calling the llseek system call - * - * Copyright (C) 1994 Remy Card. This file may be redistributed - * under the terms of the GNU Public License. - * - * This file is borrowed from the util-linux-2.10q tarball's implementation - * of fdisk. It allows seeks to 64 bit offsets, if supported. - * Changed "ext2_" prefix to "llse". - */ - -#include - -#include -#include -#include -#include /* for __NR_llseek */ - -#if defined(__GNUC__) || defined(HAS_LONG_LONG) -typedef long long llse_loff_t; -#else -typedef long llse_loff_t; -#endif - -extern llse_loff_t llse_llseek(unsigned int, llse_loff_t, unsigned int); - -#ifdef __linux__ - -#if defined(__alpha__) || defined(__ia64__) - -#ifdef __NR_lseek -static off_t my_lseek(int fd, off_t off, int whence) -{ - return syscall(__NR_lseek, fd, off, whence); -} -#else /* undefined __NR_lseek */ -static off_t my_lseek(int fd, off_t off, int whence) -{ - errno = ENOSYS; - return -1; -} -#endif /* __NR_lseek */ - -#define my_llseek my_lseek - -#else /* !__alpha__ && !__ia64__ */ - -static int _llseek(unsigned int, unsigned long, - unsigned long, llse_loff_t *, unsigned int); - -#ifndef __NR_llseek -/* no __NR_llseek on compilation machine - might give it explicitly */ -static int _llseek(unsigned int fd, unsigned long oh, - unsigned long ol, llse_loff_t * result, unsigned int origin) -{ - errno = ENOSYS; - return -1; -} -#else -static int _llseek(unsigned int fd, unsigned long oh, - unsigned long ol, llse_loff_t * result, unsigned int origin) -{ - return syscall(__NR_llseek, fd, oh, ol, result, origin); -} -#endif - -static llse_loff_t my_llseek(unsigned int fd, llse_loff_t offset, - unsigned int origin) -{ - llse_loff_t result; - int retval; - - retval = _llseek(fd, ((unsigned long long)offset) >> 32, - ((unsigned long long)offset) & 0xffffffff, - &result, origin); - return (retval == -1 ? (llse_loff_t) retval : result); -} - -#endif /* __alpha__ */ - -llse_loff_t llse_llseek(unsigned int fd, llse_loff_t offset, - unsigned int origin) -{ - llse_loff_t result; - static int do_compat = 0; - - if (!do_compat) { - result = my_llseek(fd, offset, origin); - if (!(result == -1 && errno == ENOSYS)) - return result; - - /* - * Just in case this code runs on top of an old kernel - * which does not support the llseek system call - */ - do_compat = 1; - /* - * Now try ordinary lseek. - */ - } - - if ((sizeof(off_t) >= sizeof(llse_loff_t)) || - (offset < ((llse_loff_t) 1 << ((sizeof(off_t) * 8) - 1)))) - return lseek(fd, (off_t) offset, origin); - - errno = EINVAL; - return -1; -} - -#else /* !linux */ - -llse_loff_t llse_llseek(unsigned int fd, llse_loff_t offset, - unsigned int origin) -{ - if ((sizeof(off_t) < sizeof(llse_loff_t)) && - (offset >= ((llse_loff_t) 1 << ((sizeof(off_t) * 8) - 1)))) { - errno = EINVAL; - return -1; - } - return lseek(fd, (off_t) offset, origin); -} - -#endif /* linux */ diff --git a/testcases/kernel/fs/scsi/ltpscsi/llseek.h b/testcases/kernel/fs/scsi/ltpscsi/llseek.h deleted file mode 100755 index cdf6fcea..00000000 --- a/testcases/kernel/fs/scsi/ltpscsi/llseek.h +++ /dev/null @@ -1,10 +0,0 @@ - -#if defined(__GNUC__) || defined(HAS_LONG_LONG) -typedef long long llse_loff_t; -#else -typedef long llse_loff_t; -#endif - -extern llse_loff_t llse_llseek(unsigned int fd, - llse_loff_t offset, - unsigned int origin); diff --git a/testcases/kernel/fs/scsi/ltpscsi/ltpfsscsi.sh b/testcases/kernel/fs/scsi/ltpscsi/ltpfsscsi.sh deleted file mode 100755 index 29648d9e..00000000 --- a/testcases/kernel/fs/scsi/ltpscsi/ltpfsscsi.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/sh -# This script should be run to execute the filesystem tests on SCSI vitual devices. -# 10/21/03 mridge@us.ibm.com Initial creation of testcases -# -# - -cd `dirname $0` -export LTPROOT=${PWD} -echo $LTPROOT | grep testscripts > /dev/null 2>&1 -if [ $? -eq 0 ]; then - cd .. - export LTPROOT=${PWD} -fi - -export TMPBASE="/tmp" - - -usage() -{ - cat <<-END >&2 - usage: ${0##*/} [ -a part1 ] [ -b part2 ] [ -k Kernel Path - fully qualified kernel path ] - defaults: - - There are no defaults, all items MUST be passed - - example: ${0##*/} -a sda -b sdb -k /usr/src/linux - - - These tests must be run after ssi_debug has been configured and built as a module so it can be loaded with - the correct parameters. - - - These operations are destructive so do NOT point the tests to partitions where the data shouldn't be overwritten. - Once these tests are started all data in the partitions you point to will be destroyed. - - END -exit -} - -while getopts :a:b:c:k: arg -do case $arg in - a) part1=$OPTARG;; - b) part2=$OPTARG;; - c) part3=$OPTARG;; - k) kernpath=$OPTARG;; - - \?) echo "************** Help Info: ********************" - usage;; - esac -done - -if [ ! -n "$part1" ]; then - echo "Missing 1st partition. You must pass 2 partitions for testing" - usage; - exit -fi - -if [ ! -n "$part2" ]; then - echo "Missing 2nd partition. You must pass 2 partitions for testing" - usage; - exit -fi - -if [ ! -n "$part3" ]; then - echo "Missing 3rd partition. You must pass 3 partitions for testing" - usage; - exit -fi - -if [ ! -n "$kernpath" ]; then - echo "Missing kernel path. You must pass kernel path for testing" - usage; - exit -fi - -export PATH="${PATH}:${LTPROOT}/testcases/bin" - - -mkdir /test >/dev/null 2>&1 -mkdir /test/growfiles >/dev/null 2>&1 -mkdir /test/growfiles/scsi >/dev/null 2>&1 -mkdir /test/growfiles/scsi/ext2 >/dev/null 2>&1 -mkdir /test/growfiles/scsi/ext3 >/dev/null 2>&1 -mkdir /test/growfiles/scsi/reiser >/dev/null 2>&1 - - -mkfs -V -t ext2 /dev/$part1 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sg_include.h" -#include "sg_err.h" -#include "llseek.h" - -#define ME "scsimain: " - -#define NUMERIC_SCAN_DEF 1 /* change to 0 to make alpha scan default */ -//static char * version_str = "0.21 20030513"; - -#define BPI (signed)(sizeof(int)) -#define READWRITE_BASE_NUM 0x12345678 -#define DEF_BLOCK_SIZE 512 -#define DEF_NUM_THREADS 16 -#define MAX_NUM_THREADS SG_MAX_QUEUE -#define DEF_BLOCKS_PER_TRANSFER 128 -#define DEF_SCSI_CDBSZ 10 -#define MAX_SCSI_CDBSZ 16 -#define TUR_CMD_LEN 6 -#define DEVNAME_SZ 256 -#define MAX_HOLES 4 - -#define OFF sizeof(struct sg_header) -#define INQ_REPLY_LEN 96 /* logic assumes >= sizeof(inqCmdBlk) */ -#define INQUIRY_CMDLEN 6 -#define INQUIRY_CMD 0x12 -#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ -#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */ -#define REASON_SZ 128 - -#define SENSE_BUFF_SZ 64 -#define RCAP_REPLY_LEN 8 -#define LOG_SENSE_CMD 0x4d -#define LOG_SENSE_CMDLEN 10 -#define MX_ALLOC_LEN (1024 * 17) -#define D_ROOT_SZ 512 -#define STR_SZ 1024 -#define INOUTF_SZ 512 -#define EBUFF_SZ 512 -#define MDEV_NAME_SZ 256 - -#define PG_CODE_ALL 0x00 - -#define TRUE 1 -#define FALSE 0 -#define MAX_DEVICES 50 - -#define NAME_LEN_MAX 256 -#define LEVELS 4 - -#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ -#define INQ_ALLOC_LEN 255 - -#ifndef SCSI_IOCTL_GET_PCI -#define SCSI_IOCTL_GET_PCI 0x5387 -#endif - -#define READ_CAP_REPLY_LEN 8 - -#ifndef RAW_MAJOR -#define RAW_MAJOR 255 /*unlikey value */ -#endif - -#define FT_OTHER 1 /* filetype is probably normal */ -#define FT_SG 2 /* filetype is sg char device or supports - SG_IO ioctl */ -#define FT_RAW 4 /* filetype is raw char device */ -#define FT_DEV_NULL 8 /* either "/dev/null" or "." as filename */ -#define FT_ST 16 /* filetype is st char device (tape) */ -#define FT_BLOCK 32 /* filetype is block device */ - -#define DEV_NULL_MINOR_NUM 3 - -#ifdef SG_GET_RESERVED_SIZE -#define OPEN_FLAG O_RDONLY -#else -#define OPEN_FLAG O_RDWR -#endif - -#ifndef SG_MAX_SENSE -#define SG_MAX_SENSE 16 -#endif - -#define TEST_START 0 -#define TEST_BREAK 1 -#define TEST_STOP 2 -#define MAX_SG_DEVS 128 -#define MAX_SD_DEVS 128 -#define MAX_SR_DEVS 128 -#define MAX_ST_DEVS 128 -#define MAX_OSST_DEVS 128 -#define MAX_ERRORS 5 - -#define LIN_DEV_TYPE_UNKNOWN 0 -#define LIN_DEV_TYPE_SD 1 -#define LIN_DEV_TYPE_SR 2 -#define LIN_DEV_TYPE_ST 3 -#define LIN_DEV_TYPE_SCD 4 -#define LIN_DEV_TYPE_OSST 5 - -#define MODE_SENSE6_CMD 0x1a -#define MODE_SENSE6_CMDLEN 6 -#define MODE_SENSE10_CMD 0x5a -#define MODE_SENSE10_CMDLEN 10 -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 -#define MODE_ALLOC_LEN (1024 * 4) - -#define MODE_CODE_ALL 0x3f - -#define RB_MODE_DESC 3 -#define RB_MODE_DATA 2 -#define RB_DESC_LEN 4 -#define RB_MB_TO_READ 200 -#define RB_OPCODE 0x3C -#define RB_CMD_LEN 10 - -/* #define SG_DEBUG */ - -#ifndef SG_FLAG_MMAP_IO -#define SG_FLAG_MMAP_IO 4 -#endif -#ifndef SG_SCSI_RESET -#define SG_SCSI_RESET 0x2284 -#endif - -#ifndef SG_SCSI_RESET_NOTHING -#define SG_SCSI_RESET_NOTHING 0 -#define SG_SCSI_RESET_DEVICE 1 -#define SG_SCSI_RESET_BUS 2 -#define SG_SCSI_RESET_HOST 3 -#endif -#define LONG_TIMEOUT 2400000 /* 2,400,000 millisecs == 40 minutes */ - -#define SEND_DIAGNOSTIC_CMD 0x1d -#define SEND_DIAGNOSTIC_CMDLEN 6 -#define RECEIVE_DIAGNOSTIC_CMD 0x1c -#define RECEIVE_DIAGNOSTIC_CMDLEN 6 - -#define START_STOP 0x1b -#define SYNCHRONIZE_CACHE 0x35 - -#define DEF_START_TIMEOUT 120000 /* 120,000 millisecs == 2 minutes */ - -#define DEVICE_RESET 0 -#define HOST_RESET 1 -#define BUS_RESET 2 -#define SG_HSZ sizeof(struct sg_header) -#define OFFSET_HEADER (SG_HSZ - (2 * sizeof(int))) -#define SIZEOF_BUFFER (256*1024) -#define SIZEOF_BUFFER1 (16*1024) -#define MAXPARM 32 - -#define SETUP_MODE_PAGE(NPAGE, NPARAM) \ - status = get_mode_page(NPAGE, page_code); \ - if (status) { printf("\n"); return status; } \ - bdlen = buffer[11]; \ - pagestart = buffer + 12 + bdlen; - -typedef struct request_collection { /* one instance visible to all threads */ - int infd; - int skip; - int in_type; - int in_scsi_type; - int in_blk; /* -\ next block address to read */ - int in_count; /* | blocks remaining for next read */ - int in_done_count; /* | count of completed in blocks */ - int in_partial; /* | */ - int in_stop; /* | */ - pthread_mutex_t in_mutex; /* -/ */ - int outfd; - int seek; - int out_type; - int out_scsi_type; - int out_blk; /* -\ next block address to write */ - int out_count; /* | blocks remaining for next write */ - int out_done_count; /* | count of completed out blocks */ - int out_partial; /* | */ - int out_stop; /* | */ - pthread_mutex_t out_mutex; /* | */ - pthread_cond_t out_sync_cv; /* -/ hold writes until "in order" */ - int bs; - int bpt; - int fua_mode; - int dio; - int dio_incomplete; /* -\ */ - int sum_of_resids; /* | */ - pthread_mutex_t aux_mutex; /* -/ (also serializes some printf()s */ - int coe; - int cdbsz; - int debug; -} Rq_coll; - -typedef struct request_element { /* one instance per worker thread */ - int infd; - int outfd; - int wr; - int blk; - int num_blks; - unsigned char *buffp; - unsigned char *alloc_bp; - sg_io_hdr_t io_hdr; - unsigned char cmd[MAX_SCSI_CDBSZ]; - unsigned char sb[SENSE_BUFF_LEN]; - int bs; - int fua_mode; - int dio; - int dio_incomplete; - int resid; - int in_scsi_type; - int out_scsi_type; - int cdbsz; - int debug; -} Rq_elem; - -typedef struct my_map_info { - int active; - int lin_dev_type; - int oth_dev_num; - struct sg_scsi_id sg_dat; - char vendor[8]; - char product[16]; - char revision[4]; -} my_map_info_t; - -typedef struct sg_map { - int bus; - int channel; - int target_id; - int lun; - char *dev_name; -} Sg_map; - -typedef struct my_scsi_idlun { -/* why can't userland see this structure ??? */ - int dev_id; - int host_unique_id; -} My_scsi_idlun; - -struct page_code_desc { - int page_code; - const char *desc; -}; - -static const char *pg_control_str_arr[] = { - "current", - "changeable", - "default", - "saved" -}; - -char *devices[] = - { "/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd", "/dev/sde", "/dev/sdf", - "/dev/sdg", "/dev/sdh", "/dev/sdi", "/dev/sdj", "/dev/sdk", "/dev/sdl", - "/dev/sdm", "/dev/sdn", "/dev/sdo", "/dev/sdp", "/dev/sdq", "/dev/sdr", - "/dev/sds", "/dev/sdt", "/dev/sdu", "/dev/sdv", "/dev/sdw", "/dev/sdx", - "/dev/sdy", "/dev/sdz", "/dev/sdaa", "/dev/sdab", "/dev/sdac", - "/dev/sdad", - "/dev/scd0", "/dev/scd1", "/dev/scd2", "/dev/scd3", "/dev/scd4", - "/dev/scd5", - "/dev/scd6", "/dev/scd7", "/dev/scd8", "/dev/scd9", "/dev/scd10", - "/dev/scd11", - "/dev/sr0", "/dev/sr1", "/dev/sr2", "/dev/sr3", "/dev/sr4", "/dev/sr5", - "/dev/sr6", "/dev/sr7", "/dev/sr8", "/dev/sr9", "/dev/sr10", - "/dev/sr11", - "/dev/nst0", "/dev/nst1", "/dev/nst2", "/dev/nst3", "/dev/nst4", - "/dev/nst5", - "/dev/nosst0", "/dev/nosst1", "/dev/nosst2", "/dev/nosst3", - "/dev/nosst4" -}; - -static char *page_names[] = { - NULL, - "Read-Write Error Recovery", - "Disconnect-Reconnect", - "Format Device", - "Rigid Disk Geometry", - /* "Flexible Disk" */ NULL, - NULL, - "Verify Error Recovery", - "Caching", - "Peripheral Device", - "Control Mode", - /* "Medium Types Supported" */ NULL, - "Notch and Partition", - /* "CD-ROM" */ NULL, - /* "CD-ROM Audio Control" */ NULL, - NULL, - /* "Medium Partition (1)" */ NULL, - /* "Medium Partition (2)" */ NULL, - /* "Medium Partition (3)" */ NULL, - /* "Medium Partition (4)" */ NULL -}; - -#define MAX_PAGENO (sizeof(page_names)/sizeof(char *)) - -/* Following 2 macros from D.R. Butenhof's POSIX threads book: - ISBN 0-201-63392-2 . [Highly recommended book.] */ -#define err_exit(code,text) do { \ - fprintf(stderr, "%s at \"%s\":%d: %s\n", \ - text, __FILE__, __LINE__, strerror(code)); \ - exit(1); \ - } while (0) - -static Sg_map sg_map_arr[(sizeof(devices) / sizeof(char *)) + 1]; - -static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, - 12, 12, 10, 10 -}; -const unsigned char rbCmdBlk[10] = { READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char *level_arr[LEVELS] = { "host", "bus", "target", "lun" }; - -static const char *proc_allow_dio = "/proc/scsi/sg/allow_dio"; -static const char *devfs_id = "/dev/.devfsd"; -static my_map_info_t map_arr[MAX_SG_DEVS]; -static char ebuff[EBUFF_SZ]; -static int glob_fd; -static char defectformat = 0x4; -static sigset_t signal_set; -static pthread_t sig_listen_thread_id; - -static int do_ide = 0; -static int do_inq = 1; -static int do_leaf = 1; -static int do_extra = 1; -static int do_quiet = 0; -static int checked_sg = 1; -static int sum_of_resids = 0; - -static int dd_count = -1; -static int in_full = 0; -static int in_partial = 0; -static int out_full = 0; -static int out_partial = 0; -static int do_coe = 0; -int base = READWRITE_BASE_NUM; -unsigned char *cmpbuf = 0; -static unsigned char buff_a[SIZEOF_BUFFER + SG_HSZ + 12]; -static unsigned char *buffer = buff_a + OFFSET_HEADER; - -typedef struct my_sg_scsi_id { - int host_no; /* as in "scsi" where 'n' is one of 0, 1, 2 etc */ - int channel; - int scsi_id; /* scsi id of target device */ - int lun; - int scsi_type; /* TYPE_... defined in scsi/scsi.h */ - short h_cmd_per_lun; /* host (adapter) maximum commands per lun */ - short d_queue_depth; /* device (or adapter) maximum queue length */ - int unused1; /* probably find a good use, set 0 for now */ - int unused2; /* ditto */ -} My_sg_scsi_id; - -// Prototypes -int do_scsi_sgp_read_write(char *device); -int do_scsi_sgm_read_write(char *device); -void sg_in_operation(Rq_coll * clp, Rq_elem * rep); -void sg_out_operation(Rq_coll * clp, Rq_elem * rep); -int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks); -void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks); -int sg_start_io(Rq_elem * rep); -int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp); -int run_sg_scan_tests(void); -int show_scsi_logs(char *device); -int validate_device(char *device); -int show_devfs_devices(void); -void usage(void); -int do_scsi_device_read_write(char *device); -int do_scsi_inquiry(char *device, int hex_flag); -int show_scsi_maps(void); -int show_scsi_modes(char *device); -int do_scsi_read_buffer(char *device); -int show_scsi_read_capacity(char *device); -int do_scsi_reset_devices(char *device, int reset_opts); -int do_scsi_send_diagnostics(char *device); -int do_scsi_start_stop(char *device, int startstop); -int do_scsi_read_write_buffer(char *device); -int do_scsi_test_unit_ready(char *device); -int show_scsi_info(char *device); -void print_msg(int msg_num, const char *msg); -static void scan_dev_type(const char *leadin, int max_dev, int do_numeric, - int lin_dev_type, int last_sg_ind); - -#ifdef SG_IO -int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra); -#endif - -static unsigned char inqCmdBlk[INQUIRY_CMDLEN] = - { 0x12, 0, 0, 0, INQ_REPLY_LEN, 0 }; - -void print_msg(int msg_num, const char *msg) -{ - switch (msg_num) { - case TEST_START: - printf - ("\n****************** Starting Tests ***************************\n"); - break; - case TEST_STOP: - printf - ("\n****************** Tests Complete ***************************\n"); - break; - case TEST_BREAK: - printf("\n------------------ %s Test ------------------\n\n", - msg); - break; - } -} - -int main(int argc, char *argv[]) -{ - int rc = 0; - - if (argc < 2) { - printf("\n\nERROR:No device passed to test\n\n"); - usage(); - return 1; - } - - rc = validate_device(argv[1]); - if (rc == 0) { - - print_msg(TEST_START, NULL); - - rc = run_sg_scan_tests(); - if (rc != 0) { - printf("ERROR: run_sg_scan_tests failed %d\n", rc); - } - - rc = show_scsi_logs(argv[1]); - if (rc != 0) { - printf("ERROR: show_scsi_logs failed %d\n", rc); - } - - rc = show_devfs_devices(); - if (rc != 0) { - printf("ERROR: show_devfs_devices failed %d\n", rc); - } - - rc = do_scsi_device_read_write(argv[1]); - if (rc != 0) { - printf("ERROR: do_scsi_devices_read_write failed %d\n", - rc); - } - - rc = do_scsi_inquiry(argv[1], TRUE); - if (rc != 0) { - printf("ERROR: do_scsi_inquiry HEX failed %d\n", rc); - } else { - rc = do_scsi_inquiry(argv[1], FALSE); - if (rc != 0) { - printf("ERROR: do_scsi_inquiry PCI failed %d\n", - rc); - } - } - - rc = show_scsi_maps(); - if (rc != 0) { - printf("ERROR: show_scsi_maps failed %d\n", rc); - } - - rc = show_scsi_modes(argv[1]); - if (rc != 0) { - printf("ERROR: show_scsi_modes failed %d\n", rc); - } - - rc = do_scsi_read_buffer(argv[1]); - if (rc != 0 && rc != 1) { - printf("ERROR: do_scsi_read_buffer failed %d\n", rc); - } - - rc = show_scsi_read_capacity(argv[1]); - if (rc != 0) { - printf("ERROR: show_scsi_read_capacity failed %d\n", - rc); - } - - rc |= do_scsi_reset_devices(argv[1], DEVICE_RESET); - rc |= do_scsi_reset_devices(argv[1], BUS_RESET); - rc |= do_scsi_reset_devices(argv[1], HOST_RESET); - if (rc != 0) { - printf("ERROR: do_scsi_reset_devices failed %d\n", rc); - } - - rc = do_scsi_send_diagnostics(argv[1]); - if (rc != 0) { - printf("ERROR: do_scsi_send_diagnostics failed %d\n", - rc); - } - - rc |= do_scsi_start_stop(argv[1], FALSE); - rc |= do_scsi_start_stop(argv[1], TRUE); - if (rc != 0) { - printf("ERROR: do_scsi_start_top failed %d\n", rc); - } - - rc = do_scsi_read_write_buffer(argv[1]); - if (rc != 0 && rc != 1) { - printf("ERROR: do_scsi_read_write_buffer failed %d\n", - rc); - } - - rc = do_scsi_test_unit_ready(argv[1]); - if (rc != 0) { - printf("ERROR: do_scsi_test_unit_ready failed %d\n", - rc); - } - - rc = show_scsi_info(argv[1]); - if (rc != 0) { - printf("ERROR: show_scsi_info failed %d\n", rc); - } - - rc = do_scsi_sgp_read_write(argv[1]); - if (rc != 0) { - printf("ERROR: do_scsi_sgp_read_write failed %d\n", rc); - } - - rc = do_scsi_sgm_read_write(argv[1]); - if (rc != 0) { - printf("ERROR: do_scsi_sgm_read_write failed %d\n", rc); - } - - print_msg(TEST_STOP, NULL); - } else { - printf("\nERROR: Invalid device passed to test\n\n\n"); - usage(); - - } - - return 0; -} - -int validate_device(char *device) -{ - int rc = 0; - int i, found = FALSE; - char device_string[25]; - - for (i = 0; i < MAX_DEVICES && !found; i++) { - sprintf(device_string, "/dev/sg%d", i); - //printf("checking %s \n", device_string); - if (strcmp(device, device_string) == 0) { - found = TRUE; - } - } - - return rc; -} - -void usage() -{ - printf("Usage: 'sg_scan [-a] [-n] [-w] [-i] [-x]'\n"); - printf(" where: -a do alpha scan (ie sga, sgb, sgc)\n"); - printf(" -n do numeric scan (ie sg0, sg1...) [default]\n"); - printf(" -w force open with read/write flag\n"); - printf(" -i do SCSI INQUIRY, output results\n"); - printf(" -x extra information output about queuing\n\n\n"); -} - -void make_dev_name(char *fname, const char *leadin, int k, int do_numeric) -{ - char buff[64]; - int big, little; - - strcpy(fname, leadin ? leadin : "/dev/sg"); - if (do_numeric) { - sprintf(buff, "%d", k); - strcat(fname, buff); - } else { - if (k < 26) { - buff[0] = 'a' + (char)k; - buff[1] = '\0'; - strcat(fname, buff); - } else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */ - big = k / 26; - little = k - (26 * big); - big = big - 1; - - buff[0] = 'a' + (char)big; - buff[1] = 'a' + (char)little; - buff[2] = '\0'; - strcat(fname, buff); - } else - strcat(fname, "xxxx"); - } -} - -int run_sg_scan_tests() -{ - int sg_fd, res, k, f; - unsigned char inqBuff[OFF + INQ_REPLY_LEN]; - int inqInLen = OFF + sizeof(inqCmdBlk); - int inqOutLen = OFF + INQ_REPLY_LEN; - unsigned char *buffp = inqBuff + OFF; - struct sg_header *isghp = (struct sg_header *)inqBuff; - int do_numeric = NUMERIC_SCAN_DEF; - int do_inquiry = 0; - int do_extra = 1; - int writeable = 0; - int num_errors = 0; - int num_silent = 0; - int eacces_err = 0; - char fname[64]; - My_scsi_idlun my_idlun; - int host_no; - int flags; - int emul; - - print_msg(TEST_BREAK, __FUNCTION__); - - flags = writeable ? O_RDWR : OPEN_FLAG; - - do_numeric = 1; - writeable = O_RDONLY; - do_inquiry = 1; - do_extra = 1; - - for (k = 0, res = 0; (k < 1000) && (num_errors < MAX_ERRORS); - ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) { - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", - fname); - perror(ME "close error"); - return 1; - } - make_dev_name(fname, NULL, k, do_numeric); - - sg_fd = open(fname, flags | O_NONBLOCK); - if (sg_fd < 0) { - if (EBUSY == errno) { - printf - ("%s: device busy (O_EXCL lock), skipping\n", - fname); - continue; - } else if ((ENODEV == errno) || (ENOENT == errno) || - (ENXIO == errno)) { - ++num_errors; - ++num_silent; - continue; - } else { - if (EACCES == errno) - eacces_err = 1; - snprintf(ebuff, EBUFF_SZ, - ME "Error opening %s ", fname); - perror(ebuff); - ++num_errors; - continue; - } - } - res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "device %s failed on scsi ioctl, skip", - fname); - perror(ebuff); - ++num_errors; - continue; - } - res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi " - "ioctl(2), skip", fname); - perror(ebuff); - ++num_errors; - continue; - } -#ifdef SG_EMULATED_HOST - res = ioctl(sg_fd, SG_EMULATED_HOST, &emul); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "device %s failed on sg ioctl(3), skip", - fname); - perror(ebuff); - ++num_errors; - continue; - } -#else - emul = 0; -#endif - printf("%s: scsi%d channel=%d id=%d lun=%d", fname, host_no, - (my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff, - (my_idlun.dev_id >> 8) & 0xff); - if (emul) - printf(" [em]"); -#if 0 - printf(", huid=%d", my_idlun.host_unique_id); -#endif -#ifdef SG_GET_RESERVED_SIZE - { - My_sg_scsi_id m_id; /* compatible with sg_scsi_id_t in sg.h */ - - res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "device %s ioctls(4), skip", fname); - perror(ebuff); - ++num_errors; - continue; - } - printf(" type=%d", m_id.scsi_type); - if (do_extra) - printf(" cmd_per_lun=%hd queue_depth=%hd\n", - m_id.h_cmd_per_lun, m_id.d_queue_depth); - else - printf("\n"); - } -#else - printf("\n"); -#endif - if (!do_inquiry) - continue; - -#ifdef SG_IO - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) && (f >= 30000)) { - res = sg3_inq(sg_fd, inqBuff, do_extra); - continue; - } -#endif - memset(isghp, 0, sizeof(struct sg_header)); - isghp->reply_len = inqOutLen; - memcpy(inqBuff + OFF, inqCmdBlk, INQUIRY_CMDLEN); - - if (O_RDWR == (flags & O_ACCMODE)) { /* turn on blocking */ - f = fcntl(sg_fd, F_GETFL); - fcntl(sg_fd, F_SETFL, f & (~O_NONBLOCK)); - } else { - close(sg_fd); - sg_fd = open(fname, O_RDWR); - } - - res = write(sg_fd, inqBuff, inqInLen); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, ME "device %s writing, skip", - fname); - perror(ebuff); - ++num_errors; - continue; - } - res = read(sg_fd, inqBuff, inqOutLen); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, ME "device %s reading, skip", - fname); - perror(ebuff); - ++num_errors; - continue; - } -#ifdef SG_GET_RESERVED_SIZE - if (!sg_chk_n_print("Error from Inquiry", isghp->target_status, - isghp->host_status, isghp->driver_status, - isghp->sense_buffer, SG_MAX_SENSE)) - continue; -#else - if ((isghp->result != 0) || (0 != isghp->sense_buffer[0])) { - printf("Error from Inquiry: result=%d\n", - isghp->result); - if (0 != isghp->sense_buffer[0]) - sg_print_sense("Error from Inquiry", - isghp->sense_buffer, - SG_MAX_SENSE); - continue; - } -#endif - f = (int)*(buffp + 7); - printf(" %.8s %.16s %.4s ", buffp + 8, buffp + 16, - buffp + 32); - printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x]\n", - ! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1), - (*buffp & 0xe0) >> 5); - } - if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) { - printf("Stopping because there are too many error\n"); - if (eacces_err) - printf(" root access may be required\n"); - } - return 0; -} - -#ifdef SG_IO -int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra) -{ - sg_io_hdr_t io_hdr; - unsigned char sense_buffer[32]; - int ok; - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(inqCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_buffer); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = INQ_REPLY_LEN; - io_hdr.dxferp = inqBuff; - io_hdr.cmdp = inqCmdBlk; - io_hdr.sbp = sense_buffer; - io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */ - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror(ME "Inquiry SG_IO ioctl error"); - return 1; - } - - /* now for the error processing */ - ok = 0; - switch (sg_err_category3(&io_hdr)) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - ok = 1; - break; - default: /* won't bother decoding other categories */ - sg_chk_n_print3("INQUIRY command error", &io_hdr); - break; - } - - if (ok) { /* output result if it is available */ - char *p = (char *)inqBuff; - int f = (int)*(p + 7); - printf(" %.8s %.16s %.4s ", p + 8, p + 16, p + 32); - printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x] ", - ! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1), - (*p & 0xe0) >> 5); - if (do_extra) - printf("dur=%ums\n", io_hdr.duration); - else - printf("\n"); - } - return 0; -} -#endif - -static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code, - int paramp, void *resp, int mx_resp_len, int noisy) -{ - int res; - unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] = - { LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0)); - logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); - logsCmdBlk[5] = (unsigned char)((paramp >> 8) & 0xff); - logsCmdBlk[6] = (unsigned char)(paramp & 0xff); - if (mx_resp_len > 0xffff) { - printf(ME "mx_resp_len too big\n"); - return -1; - } - logsCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); - logsCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(logsCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = logsCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (log sense) error"); - return -1; - } -#if 0 - printf("SG_IO ioctl: status=%d, info=%d, sb_len_wr=%d\n", - io_hdr.status, io_hdr.info, io_hdr.sb_len_wr); -#endif - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - if (noisy) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, ME "ppc=%d, sp=%d, " - "pc=%d, page_code=%x, paramp=%x\n ", ppc, - sp, pc, pg_code, paramp); - sg_chk_n_print3(ebuff, &io_hdr); - } - return -1; - } -} - -static void dStrHex(const char *str, int len, int no_ascii) -{ - const char *p = str; - unsigned char c; - char buff[82]; - int a = 0; - const int bpstart = 5; - const int cpstart = 60; - int cpos = cpstart; - int bpos = bpstart; - int i, k; - - if (len <= 0) - return; - memset(buff, ' ', 80); - buff[80] = '\0'; - k = sprintf(buff + 1, "%.2x", a); - buff[k + 1] = ' '; - if (bpos >= ((bpstart + (9 * 3)))) - bpos++; - - for (i = 0; i < len; i++) { - c = *p++; - bpos += 3; - if (bpos == (bpstart + (9 * 3))) - bpos++; - sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); - buff[bpos + 2] = ' '; - if (no_ascii) - buff[cpos++] = ' '; - else { - if ((c < ' ') || (c >= 0x7f)) - c = '.'; - buff[cpos++] = c; - } - if (cpos > (cpstart + 15)) { - printf("%s\n", buff); - bpos = bpstart; - cpos = cpstart; - a += 16; - memset(buff, ' ', 80); - k = sprintf(buff + 1, "%.2x", a); - buff[k + 1] = ' '; - } - } - if (cpos > cpstart) { - printf("%s\n", buff); - } -} - -static void show_page_name(int page_no) -{ - switch (page_no) { - case 0x0: - printf(" 0x00 Supported log pages\n"); - break; - case 0x1: - printf(" 0x01 Buffer over-run/under-run\n"); - break; - case 0x2: - printf(" 0x02 Error counters (write)\n"); - break; - case 0x3: - printf(" 0x03 Error counters (read)\n"); - break; - case 0x4: - printf(" 0x04 Error counters (read reverse)\n"); - break; - case 0x5: - printf(" 0x05 Error counters (verify)\n"); - break; - case 0x6: - printf(" 0x06 Non-medium errors\n"); - break; - case 0x7: - printf(" 0x07 Last n error events\n"); - break; - case 0x8: - printf(" 0x08 Format status (sbc2)\n"); - break; - case 0xb: - printf(" 0x0b Last n deferred errors of " - "asynchronous events\n"); - break; - case 0xc: - printf(" 0x0c Sequential Access (ssc-2)\n"); - break; - case 0xd: - printf(" 0x0d Temperature\n"); - break; - case 0xe: - printf(" 0x0e Start-stop cycle counter\n"); - break; - case 0xf: - printf(" 0x0f Application client\n"); - break; - case 0x10: - printf(" 0x10 Self-test results\n"); - break; - case 0x18: - printf(" 0x18 Protocol specific port\n"); - break; - case 0x2e: - printf(" 0x2e Tape alerts (ssc-2)\n"); - break; - case 0x2f: - printf(" 0x2f Informational exceptions (SMART)\n"); - break; - default: - printf(" 0x%.2x\n", page_no); - break; - } -} - -static void show_buffer_under_overrun_page(unsigned char *resp, int len) -{ - int k, j, num, pl, count_basis, cause; - unsigned char *ucp; - unsigned char *xp; - unsigned long long ull; - - printf("Buffer over-run/under-run page\n"); - num = len - 4; - ucp = &resp[0] + 4; - while (num > 3) { - pl = ucp[3] + 4; - count_basis = (ucp[1] >> 5) & 0x7; - printf(" Count basis: "); - switch (count_basis) { - case 0: - printf("undefined"); - break; - case 1: - printf("per command"); - break; - case 2: - printf("per failed reconnect"); - break; - case 3: - printf("per unit of time"); - break; - default: - printf("reserved [0x%x]", count_basis); - break; - } - cause = (ucp[1] >> 1) & 0xf; - printf(", Cause: "); - switch (cause) { - case 0: - printf("bus busy"); - break; - case 1: - printf("transfer rate too slow"); - break; - default: - printf("reserved [0x%x]", cause); - break; - } - printf(", Type: "); - if (ucp[1] & 1) - printf("over-run"); - else - printf("under-run"); - printf(", count"); - k = pl - 4; - xp = ucp + 4; - if (k > sizeof(ull)) { - xp += (k - sizeof(ull)); - k = sizeof(ull); - } - ull = 0; - for (j = 0; j < k; ++j) { - if (j > 0) - ull <<= 8; - ull |= xp[j]; - } - printf(" = %llu\n", ull); - num -= pl; - ucp += pl; - } -} - -static void show_error_counter_page(unsigned char *resp, int len) -{ - int k, j, num, pl, pc; - unsigned char *ucp; - unsigned char *xp; - unsigned long long ull; - - switch (resp[0]) { - case 2: - printf("Write error counter page\n"); - break; - case 3: - printf("Read error counter page\n"); - break; - case 4: - printf("Read Reverse error counter page\n"); - break; - case 5: - printf("Verify error counter page\n"); - break; - default: - printf("expecting error counter page, got page=0x%x\n", - resp[0]); - return; - } - num = len - 4; - ucp = &resp[0] + 4; - while (num > 3) { - pc = (ucp[0] << 8) | ucp[1]; - pl = ucp[3] + 4; - switch (pc) { - case 0: - printf(" Errors corrected without substantion delay"); - break; - case 1: - printf(" Errors corrected with possible delays"); - break; - case 2: - printf(" Total operations"); - break; - case 3: - printf(" Total errors corrected"); - break; - case 4: - printf(" Total times correction algorithm processed"); - break; - case 5: - printf(" Total bytes processed"); - break; - case 6: - printf(" Total uncorrected errors"); - break; - default: - printf(" Reserved or vendor specific [0x%x]", pc); - break; - } - k = pl - 4; - xp = ucp + 4; - if (k > sizeof(ull)) { - xp += (k - sizeof(ull)); - k = sizeof(ull); - } - ull = 0; - for (j = 0; j < k; ++j) { - if (j > 0) - ull <<= 8; - ull |= xp[j]; - } - printf(" = %llu\n", ull); - num -= pl; - ucp += pl; - } -} - -static void show_non_medium_error_page(unsigned char *resp, int len) -{ - int k, j, num, pl, pc; - unsigned char *ucp; - unsigned char *xp; - unsigned long long ull; - - printf("Non-medium error page\n"); - num = len - 4; - ucp = &resp[0] + 4; - while (num > 3) { - pc = (ucp[0] << 8) | ucp[1]; - pl = ucp[3] + 4; - switch (pc) { - case 0: - printf(" Non-medium error count"); - break; - default: - if (pc <= 0x7fff) - printf(" Reserved [0x%x]", pc); - else - printf(" Vendor specific [0x%x]", pc); - break; - } - k = pl - 4; - xp = ucp + 4; - if (k > sizeof(ull)) { - xp += (k - sizeof(ull)); - k = sizeof(ull); - } - ull = 0; - for (j = 0; j < k; ++j) { - if (j > 0) - ull <<= 8; - ull |= xp[j]; - } - printf(" = %llu\n", ull); - num -= pl; - ucp += pl; - } -} - -const char *self_test_code[] = { - "default", "background short", "background extended", "reserved", - "aborted background", "foreground short", "foreground extended", - "reserved" -}; - -const char *self_test_result[] = { - "completed without error", - "aborted by SEND DIAGNOSTIC", - "aborted other than by SEND DIAGNOSTIC", - "unknown error, unable to complete", - "self test completed with failure in test segment (which one unkown)", - "first segment in self test failed", - "second segment in self test failed", - "another segment in self test failed", - "reserved", "reserved", "reserved", "reserved", "reserved", "reserved", - "reserved", - "self test in progress" -}; - -static void show_self_test_page(unsigned char *resp, int len) -{ - int k, num, n, res; - unsigned char *ucp; - unsigned long long ull; - - num = len - 4; - if (num < 0x190) { - printf("badly formed self-test results page\n"); - return; - } - printf("Self-test results page\n"); - for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20) { - n = (ucp[6] << 8) | ucp[7]; - if ((0 == n) && (0 == ucp[4])) - break; - printf(" Parameter code=%d, accumulated power-on hours=%d\n", - (ucp[0] << 8) | ucp[1], n); - printf(" self test code: %s [%d]\n", - self_test_code[(ucp[4] >> 5) & 0x7], - (ucp[4] >> 5) & 0x7); - res = ucp[4] & 0xf; - printf(" self test result: %s [%d]\n", - self_test_result[res], res); - if (ucp[5]) - printf(" self-test number=%d\n", (int)ucp[5]); - ull = ucp[8]; - ull <<= 8; - ull |= ucp[9]; - ull <<= 8; - ull |= ucp[10]; - ull <<= 8; - ull |= ucp[11]; - ull <<= 8; - ull |= ucp[12]; - ull <<= 8; - ull |= ucp[13]; - ull <<= 8; - ull |= ucp[14]; - ull <<= 8; - ull |= ucp[14]; - ull <<= 8; - ull |= ucp[15]; - if ((0xffffffffffffffffULL != ull) && (res > 0) && (res < 0xf)) - printf(" address of first error=0x%llx\n", ull); - if (ucp[16] & 0xf) - printf(" sense key=0x%x, asc=0x%x, asq=0x%x\n", - ucp[16] & 0xf, ucp[17], ucp[18]); - } -} - -static void show_Temperature_page(unsigned char *resp, int len, int hdr) -{ - int k, num, extra, pc; - unsigned char *ucp; - - num = len - 4; - ucp = &resp[0] + 4; - if (num < 4) { - printf("badly formed Temperature log page\n"); - return; - } - if (hdr) - printf("Temperature log page\n"); - for (k = num; k > 0; k -= extra, ucp += extra) { - if (k < 3) { - printf("short Temperature log page\n"); - return; - } - extra = ucp[3] + 4; - pc = ((ucp[0] << 8) & 0xff) + ucp[1]; - if (0 == pc) { - if (extra > 5) { - if (ucp[5] < 0xff) - printf(" Current temperature= %d C\n", - ucp[5]); - else - printf - (" Current temperature=\n"); - } - } else if (1 == pc) { - if (extra > 5) { - if (ucp[5] < 0xff) - printf - (" Reference temperature= %d C\n", - ucp[5]); - else - printf - (" Reference temperature=\n"); - } - - } else { - printf(" parameter code=0x%x, contents in hex:\n", pc); - dStrHex((const char *)ucp, extra, 1); - } - } -} - -static void show_IE_page(unsigned char *resp, int len, int full) -{ - int k, num, extra, pc; - unsigned char *ucp; - - num = len - 4; - ucp = &resp[0] + 4; - if (num < 4) { - printf("badly formed Informational Exceptions log page\n"); - return; - } - if (full) - printf("Informational Exceptions log page\n"); - for (k = num; k > 0; k -= extra, ucp += extra) { - if (k < 3) { - printf("short Informational Exceptions log page\n"); - return; - } - extra = ucp[3] + 4; - pc = ((ucp[0] << 8) & 0xff) + ucp[1]; - if (0 == pc) { - if (extra > 5) { - if (full) - printf(" IE asc=0x%x, ascq=0x%x", - ucp[4], ucp[5]); - if (extra > 6) { - if (full) - printf(","); - if (ucp[6] < 0xff) - printf - (" Current temperature=%d C", - ucp[6]); - else - printf - (" Current temperature="); - } - printf("\n"); - } - } else if (full) { - printf(" parameter code=0x%x, contents in hex:\n", pc); - dStrHex((const char *)ucp, extra, 1); - } - } -} - -static void show_ascii_page(unsigned char *resp, int len) -{ - int k, n, num; - - if (len < 0) { - printf("response has bad length\n"); - return; - } - num = len - 4; - switch (resp[0]) { - case 0: - printf("Supported pages:\n"); - for (k = 0; k < num; ++k) - show_page_name((int)resp[4 + k]); - break; - case 0x1: - show_buffer_under_overrun_page(resp, len); - break; - case 0x2: - case 0x3: - case 0x4: - case 0x5: - show_error_counter_page(resp, len); - break; - case 0x6: - show_non_medium_error_page(resp, len); - break; - case 0xd: - show_Temperature_page(resp, len, 1); - break; - case 0xe: - if (len < 40) { - printf("badly formed start-stop cycle counter page\n"); - break; - } - printf("Start-stop cycle counter page\n"); - printf(" Date of manufacture, year: %.4s, week: %.2s\n", - &resp[8], &resp[12]); - printf(" Accounting date, year: %.4s, week: %.2s\n", - &resp[18], &resp[22]); - n = (resp[28] << 24) | (resp[29] << 16) | (resp[30] << 8) | - resp[31]; - printf(" Specified cycle count over device lifetime=%d\n", n); - n = (resp[36] << 24) | (resp[37] << 16) | (resp[38] << 8) | - resp[39]; - printf(" Accumulated start-stop cycles=%d\n", n); - break; - case 0x10: - show_self_test_page(resp, len); - break; - case 0x2f: - show_IE_page(resp, len, 1); - break; - default: - printf("No ascii information for page=0x%x, here is hex:\n", - resp[0]); - dStrHex((const char *)resp, len, 1); - break; - } -} - -static int fetchTemperature(int sg_fd, int do_hex, unsigned char *resp, - int max_len) -{ - int res = 0; - - if (0 == do_logs(sg_fd, 0, 0, 1, 0xd, 0, resp, max_len, 0)) - show_Temperature_page(resp, (resp[2] << 8) + resp[3] + 4, 0); - else if (0 == do_logs(sg_fd, 0, 0, 1, 0x2f, 0, resp, max_len, 0)) - show_IE_page(resp, (resp[2] << 8) + resp[3] + 4, 0); - else { - printf - ("Unable to find temperature in either log page (temperature " - "or IE)\n"); - res = 1; - } - close(sg_fd); - return res; -} - -int show_scsi_logs(char *device) -{ - int sg_fd, k, pg_len; - char *file_name = 0; - unsigned char rsp_buff[MX_ALLOC_LEN]; - int pg_code = 0; - int pc = 1; /* N.B. some disks only give data for current cumulative */ - int paramp = 0; - int do_list = 0; - int do_ppc = 0; - int do_sp = 0; - int do_hex = 0; - int do_all = 1; - int do_temp = 0; - int oflags = O_RDWR | O_NONBLOCK; - - file_name = device; - print_msg(TEST_BREAK, __FUNCTION__); - - if ((sg_fd = open(file_name, oflags)) < 0) { - snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", - file_name); - perror(ebuff); - return 1; - } - /* Just to be safe, check we have a new sg device by trying an ioctl */ - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { - printf(ME "%s doesn't seem to be a version 3 sg device\n", - file_name); - close(sg_fd); - return 1; - } - if (do_list || do_all) - pg_code = PG_CODE_ALL; - pg_len = 0; - if (1 == do_temp) - return fetchTemperature(sg_fd, do_hex, rsp_buff, MX_ALLOC_LEN); - - if (0 == do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp, - rsp_buff, MX_ALLOC_LEN, 1)) { - pg_len = (rsp_buff[2] << 8) + rsp_buff[3]; - if ((pg_len + 4) > MX_ALLOC_LEN) { - printf - ("Only fetched %d bytes of response, truncate output\n", - MX_ALLOC_LEN); - pg_len = MX_ALLOC_LEN - 4; - } - if (do_hex) { - printf("Returned log page code=0x%x, page len=0x%x\n", - rsp_buff[0], pg_len); - dStrHex((const char *)rsp_buff, pg_len + 4, 1); - } else - show_ascii_page(rsp_buff, pg_len + 4); - } - if (do_all && (pg_len > 1)) { - int my_len = pg_len - 1; - unsigned char parr[256]; - - memcpy(parr, rsp_buff + 5, my_len); - for (k = 0; k < my_len; ++k) { - printf("\n"); - pg_code = parr[k]; - if (0 == - do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp, - rsp_buff, MX_ALLOC_LEN, 1)) { - pg_len = (rsp_buff[2] << 8) + rsp_buff[3]; - if ((pg_len + 4) > MX_ALLOC_LEN) { - printf - ("Only fetched %d bytes of response, truncate " - "output\n", MX_ALLOC_LEN); - pg_len = MX_ALLOC_LEN - 4; - } - if (do_hex) { - printf - ("Returned log page code=0x%x, page len=0x%x\n", - rsp_buff[0], pg_len); - dStrHex((const char *)rsp_buff, - pg_len + 4, 1); - } else - show_ascii_page(rsp_buff, pg_len + 4); - } - } - } - close(sg_fd); - return 0; -} - -static int do_inquiry(int sg_fd, void *resp, int mx_resp_len) -{ - int res; - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = - { INQUIRY_CMD, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - inqCmdBlk[4] = (unsigned char)mx_resp_len; - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(inqCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_TO_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = inqCmdBlk; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (inquiry) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - sg_chk_n_print3("Failed INQUIRY", &io_hdr); - return -1; - } -} - -void leaf_dir(const char *lf, unsigned int *larr) -{ - char name[NAME_LEN_MAX * 2]; - int res; - - if (do_quiet) { - printf("%u\t%u\t%u\t%u\n", larr[0], larr[1], larr[2], larr[3]); - return; - } - printf("%u\t%u\t%u\t%u\t%s\n", larr[0], larr[1], larr[2], larr[3], lf); - if (do_leaf) { - struct dirent *de_entry; - struct dirent *de_result; - DIR *sdir; - int outpos; - - if (NULL == (sdir = opendir(lf))) { - fprintf(stderr, "leaf_dir: opendir of %s: failed\n", - lf); - return; - } - de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX); - if (NULL == de_entry) - return; - res = 0; - printf("\t"); - outpos = 8; - while (1) { - res = readdir_r(sdir, de_entry, &de_result); - if (0 != res) { - fprintf(stderr, - "leaf_dir: readdir_r of %s: %s\n", lf, - strerror(res)); - res = -2; - break; - } - if (de_result == NULL) - break; - strncpy(name, de_entry->d_name, NAME_LEN_MAX * 2); - if ((0 == strcmp("..", name)) - || (0 == strcmp(".", name))) - continue; - if (do_extra) { - struct stat st; - char devname[NAME_LEN_MAX * 2]; - - strncpy(devname, lf, NAME_LEN_MAX * 2); - strcat(devname, "/"); - strcat(devname, name); - if (stat(devname, &st) < 0) - return; - if (S_ISCHR(st.st_mode)) { - strcat(name, "(c "); - sprintf(name + strlen(name), "%d %d)", - major(st.st_rdev), - minor(st.st_rdev)); - } else if (S_ISBLK(st.st_mode)) { - strcat(name, "(b "); - sprintf(name + strlen(name), "%d %d)", - major(st.st_rdev), - minor(st.st_rdev)); - } - } - res = strlen(name); - if ((outpos + res + 2) > 80) { - printf("\n\t"); - outpos = 8; - } - printf("%s ", name); - outpos += res + 2; - } - printf("\n"); - } - if (do_inq) { - int sg_fd; - char buff[64]; - - memset(buff, 0, sizeof(buff)); - strncpy(name, lf, NAME_LEN_MAX * 2); - strcat(name, "/generic"); - if ((sg_fd = open(name, O_RDONLY)) < 0) { - if (!checked_sg) { - checked_sg = 1; - if ((sg_fd = open("/dev/sg0", O_RDONLY)) >= 0) - close(sg_fd); /* try and get sg module loaded */ - sg_fd = open(name, O_RDONLY); - } - if (sg_fd < 0) { - printf("Unable to open sg device: %s, %s\n", - name, strerror(errno)); - return; - } - } - if (0 != do_inquiry(sg_fd, buff, 64)) - return; - close(sg_fd); - dStrHex(buff, 64, 0); - } -} - -/* Return 0 -> ok, -1 -> opendir() error, -2 -> readdir_r error, - -3 -> malloc error */ -int hbtl_scan(const char *path, int level, unsigned int *larr) -{ - struct dirent *de_entry; - struct dirent *de_result; - char new_path[NAME_LEN_MAX * 2]; - DIR *sdir; - int res; - size_t level_slen; - - level_slen = strlen(level_arr[level]); - if (NULL == (sdir = opendir(path))) { - fprintf(stderr, "hbtl_scan: opendir of %s: failed\n", path); - return -1; - } - de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX); - if (NULL == de_entry) - return -3; - res = 0; - while (1) { - res = readdir_r(sdir, de_entry, &de_result); - if (0 != res) { - fprintf(stderr, "hbtl_scan: readdir_r of %s: %s\n", - path, strerror(res)); - res = -2; - break; - } - if (de_result == NULL) - break; - if (0 == - strncmp(level_arr[level], de_entry->d_name, level_slen)) { - if (1 != - sscanf(de_entry->d_name + level_slen, "%u", - larr + level)) - larr[level] = UINT_MAX; - strncpy(new_path, path, NAME_LEN_MAX * 2); - strcat(new_path, "/"); - strcat(new_path, de_entry->d_name); - if ((level + 1) < LEVELS) { - res = hbtl_scan(new_path, level + 1, larr); - if (res < 0) - break; - } else - leaf_dir(new_path, larr); - } - } - free(de_entry); - closedir(sdir); - return res; -} - -int show_devfs_devices() -{ - int res; - char ds_root[D_ROOT_SZ]; - char di_root[D_ROOT_SZ]; - unsigned int larr[LEVELS]; - struct stat st; - - print_msg(TEST_BREAK, __FUNCTION__); - strncpy(ds_root, "/dev", D_ROOT_SZ); - - strncpy(di_root, ds_root, D_ROOT_SZ); - - strcat(di_root, "/.devfsd"); - - if (stat(di_root, &st) < 0) { - printf("Didn't find %s so perhaps devfs is not present," - " attempting to continue ...\n", di_root); - } - - strncpy(di_root, ds_root, D_ROOT_SZ); - strcat(ds_root, "/scsi"); - strcat(di_root, "/ide"); - - if (!do_ide) - printf("SCSI scan:\n"); - - res = hbtl_scan(ds_root, 0, larr); - - if (res < 0) - printf("main: scsi hbtl_scan res=%d\n", res); - - do_ide = TRUE; - do_inq = 0; /* won't try SCSI INQUIRY on IDE devices */ - - if (do_ide) { - printf("\nIDE scan:\n"); - res = hbtl_scan(di_root, 0, larr); - - if (res < 0) - printf("main: ide hbtl_scan res=%d\n", res); - } - return 0; -} - -static void install_handler(int sig_num, void (*sig_handler) (int sig)) -{ - struct sigaction sigact; - sigaction(sig_num, NULL, &sigact); - if (sigact.sa_handler != SIG_IGN) { - sigact.sa_handler = sig_handler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(sig_num, &sigact, NULL); - } -} - -void print_stats() -{ - if (0 != dd_count) - fprintf(stderr, " remaining block count=%d\n", dd_count); - fprintf(stderr, "%d+%d records in\n", in_full - in_partial, in_partial); - fprintf(stderr, "%d+%d records out\n", out_full - out_partial, - out_partial); -} - -static void interrupt_handler(int sig) -{ - struct sigaction sigact; - - sigact.sa_handler = SIG_DFL; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(sig, &sigact, NULL); - fprintf(stderr, "Interrupted by signal,"); - print_stats(); - kill(getpid(), sig); -} - -static void siginfo_handler(int sig) -{ - fprintf(stderr, "Progress report, continuing ...\n"); - print_stats(); -} - -int dd_filetype(const char *filename) -{ - struct stat st; - size_t len = strlen(filename); - - if ((1 == len) && ('.' == filename[0])) - return FT_DEV_NULL; - if (stat(filename, &st) < 0) - return FT_OTHER; - if (S_ISCHR(st.st_mode)) { - if ((MEM_MAJOR == major(st.st_rdev)) && - (DEV_NULL_MINOR_NUM == minor(st.st_rdev))) - return FT_DEV_NULL; - if (RAW_MAJOR == major(st.st_rdev)) - return FT_RAW; - if (SCSI_GENERIC_MAJOR == major(st.st_rdev)) - return FT_SG; - if (SCSI_TAPE_MAJOR == major(st.st_rdev)) - return FT_ST; - } else if (S_ISBLK(st.st_mode)) - return FT_BLOCK; - return FT_OTHER; -} - -int read_capacity(int sg_fd, int *num_sect, int *sect_sz) -{ - int res; - unsigned char rcCmdBlk[10] = - { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - unsigned char rcBuff[READ_CAP_REPLY_LEN]; - unsigned char sense_b[64]; - sg_io_hdr_t io_hdr; - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(rcCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = sizeof(rcBuff); - io_hdr.dxferp = rcBuff; - io_hdr.cmdp = rcCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("read_capacity (SG_IO) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - if (SG_ERR_CAT_MEDIA_CHANGED == res) - return 2; /* probably have another go ... */ - else if (SG_ERR_CAT_CLEAN != res) { - sg_chk_n_print3("read capacity", &io_hdr); - return -1; - } - *num_sect = 1 + ((rcBuff[0] << 24) | (rcBuff[1] << 16) | - (rcBuff[2] << 8) | rcBuff[3]); - *sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) | - (rcBuff[6] << 8) | rcBuff[7]; - return 0; -} - -/* Return of 0 -> success, -1 -> failure, 2 -> try again */ -int sync_cache(int sg_fd) -{ - int res; - unsigned char scCmdBlk[10] = { SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - }; - unsigned char sense_b[64]; - sg_io_hdr_t io_hdr; - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(scCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_NONE; - io_hdr.dxfer_len = 0; - io_hdr.dxferp = NULL; - io_hdr.cmdp = scCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("synchronize_cache (SG_IO) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - if (SG_ERR_CAT_MEDIA_CHANGED == res) - return 2; /* probably have another go ... */ - else if (SG_ERR_CAT_CLEAN != res) { - sg_chk_n_print3("synchronize cache", &io_hdr); - return -1; - } - return 0; -} - -int sg_build_scsi_cdb(unsigned char *cdbp, int cdb_sz, unsigned int blocks, - unsigned int start_block, int write_true, int fua, - int dpo) -{ - int rd_opcode[] = { 0x8, 0x28, 0xa8, 0x88 }; - int wr_opcode[] = { 0xa, 0x2a, 0xaa, 0x8a }; - int sz_ind; - - memset(cdbp, 0, cdb_sz); - if (dpo) - cdbp[1] |= 0x10; - if (fua) - cdbp[1] |= 0x8; - switch (cdb_sz) { - case 6: - sz_ind = 0; - cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : - rd_opcode[sz_ind]); - cdbp[1] = (unsigned char)((start_block >> 16) & 0x1f); - cdbp[2] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[3] = (unsigned char)(start_block & 0xff); - cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks; - if (blocks > 256) { - fprintf(stderr, - ME "for 6 byte commands, maximum number of " - "blocks is 256\n"); - return 1; - } - if ((start_block + blocks - 1) & (~0x1fffff)) { - fprintf(stderr, - ME "for 6 byte commands, can't address blocks" - " beyond %d\n", 0x1fffff); - return 1; - } - if (dpo || fua) { - fprintf(stderr, - ME "for 6 byte commands, neither dpo nor fua" - " bits supported\n"); - return 1; - } - break; - case 10: - sz_ind = 1; - cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : - rd_opcode[sz_ind]); - cdbp[2] = (unsigned char)((start_block >> 24) & 0xff); - cdbp[3] = (unsigned char)((start_block >> 16) & 0xff); - cdbp[4] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[5] = (unsigned char)(start_block & 0xff); - cdbp[7] = (unsigned char)((blocks >> 8) & 0xff); - cdbp[8] = (unsigned char)(blocks & 0xff); - if (blocks & (~0xffff)) { - fprintf(stderr, - ME "for 10 byte commands, maximum number of " - "blocks is %d\n", 0xffff); - return 1; - } - break; - case 12: - sz_ind = 2; - cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : - rd_opcode[sz_ind]); - cdbp[2] = (unsigned char)((start_block >> 24) & 0xff); - cdbp[3] = (unsigned char)((start_block >> 16) & 0xff); - cdbp[4] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[5] = (unsigned char)(start_block & 0xff); - cdbp[6] = (unsigned char)((blocks >> 24) & 0xff); - cdbp[7] = (unsigned char)((blocks >> 16) & 0xff); - cdbp[8] = (unsigned char)((blocks >> 8) & 0xff); - cdbp[9] = (unsigned char)(blocks & 0xff); - break; - case 16: - sz_ind = 3; - cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] : - rd_opcode[sz_ind]); - /* can't cope with block number > 32 bits (yet) */ - cdbp[6] = (unsigned char)((start_block >> 24) & 0xff); - cdbp[7] = (unsigned char)((start_block >> 16) & 0xff); - cdbp[8] = (unsigned char)((start_block >> 8) & 0xff); - cdbp[9] = (unsigned char)(start_block & 0xff); - cdbp[10] = (unsigned char)((blocks >> 24) & 0xff); - cdbp[11] = (unsigned char)((blocks >> 16) & 0xff); - cdbp[12] = (unsigned char)((blocks >> 8) & 0xff); - cdbp[13] = (unsigned char)(blocks & 0xff); - break; - default: - fprintf(stderr, - ME "expected cdb size of 6, 10, 12, or 16 but got" - "=%d\n", cdb_sz); - return 1; - } - return 0; -} - -/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), - 2 -> try again */ -int sg_read(int sg_fd, unsigned char *buff, int blocks, int from_block, - int bs, int cdbsz, int fua, int *diop) -{ - unsigned char rdCmd[MAX_SCSI_CDBSZ]; - unsigned char senseBuff[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) { - fprintf(stderr, - ME "bad rd cdb build, from_block=%d, blocks=%d\n", - from_block, blocks); - return -1; - } - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = cdbsz; - io_hdr.cmdp = rdCmd; - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = bs * blocks; - io_hdr.dxferp = buff; - io_hdr.mx_sb_len = SENSE_BUFF_LEN; - io_hdr.sbp = senseBuff; - io_hdr.timeout = DEF_TIMEOUT; - io_hdr.pack_id = from_block; - if (diop && *diop) - io_hdr.flags |= SG_FLAG_DIRECT_IO; - - if (ioctl(sg_fd, SG_IO, &io_hdr)) { - if (ENOMEM == errno) - return 1; - perror("reading (SG_IO) on sg device, error"); - return -1; - } - switch (sg_err_category3(&io_hdr)) { - case SG_ERR_CAT_CLEAN: - break; - case SG_ERR_CAT_RECOVERED: - fprintf(stderr, - "Recovered error while reading block=%d, num=%d\n", - from_block, blocks); - break; - case SG_ERR_CAT_MEDIA_CHANGED: - return 2; - default: - sg_chk_n_print3("reading", &io_hdr); - if (do_coe) { - memset(buff, 0, bs * blocks); - fprintf(stderr, ">> unable to read at blk=%d for " - "%d bytes, use zeros\n", from_block, - bs * blocks); - return 0; /* fudge success */ - } else - return -1; - } - if (diop && *diop && - ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) - *diop = 0; /* flag that dio not done (completely) */ - sum_of_resids += io_hdr.resid; -#if SG_DEBUG - fprintf(stderr, "duration=%u ms\n", io_hdr.duration); -#endif - return 0; -} - -/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), - 2 -> try again */ -int sg_write(int sg_fd, unsigned char *buff, int blocks, int to_block, - int bs, int cdbsz, int fua, int *diop) -{ - unsigned char wrCmd[MAX_SCSI_CDBSZ]; - unsigned char senseBuff[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) { - fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n", - to_block, blocks); - return -1; - } - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = cdbsz; - io_hdr.cmdp = wrCmd; - io_hdr.dxfer_direction = SG_DXFER_TO_DEV; - io_hdr.dxfer_len = bs * blocks; - io_hdr.dxferp = buff; - io_hdr.mx_sb_len = SENSE_BUFF_LEN; - io_hdr.sbp = senseBuff; - io_hdr.timeout = DEF_TIMEOUT; - io_hdr.pack_id = to_block; - if (diop && *diop) - io_hdr.flags |= SG_FLAG_DIRECT_IO; - - if (ioctl(sg_fd, SG_IO, &io_hdr)) { - if (ENOMEM == errno) - return 1; - perror("writing (SG_IO) on sg device, error"); - return -1; - } - switch (sg_err_category3(&io_hdr)) { - case SG_ERR_CAT_CLEAN: - break; - case SG_ERR_CAT_RECOVERED: - fprintf(stderr, - "Recovered error while writing block=%d, num=%d\n", - to_block, blocks); - break; - case SG_ERR_CAT_MEDIA_CHANGED: - return 2; - default: - sg_chk_n_print3("writing", &io_hdr); - if (do_coe) { - fprintf(stderr, ">> ignored errors for out blk=%d for " - "%d bytes\n", to_block, bs * blocks); - return 0; /* fudge success */ - } else - return -1; - } - if (diop && *diop && - ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) - *diop = 0; /* flag that dio not done (completely) */ - return 0; -} - -int get_num(char *buf) -{ - int res, num; - char c; - - res = sscanf(buf, "%d%c", &num, &c); - if (0 == res) - return -1; - else if (1 == res) - return num; - else { - switch (c) { - case 'c': - case 'C': - return num; - case 'b': - case 'B': - return num * 512; - case 'k': - return num * 1024; - case 'K': - return num * 1000; - case 'm': - return num * 1024 * 1024; - case 'M': - return num * 1000000; - case 'g': - return num * 1024 * 1024 * 1024; - case 'G': - return num * 1000000000; - default: - fprintf(stderr, "unrecognized multiplier\n"); - return -1; - } - } -} - -int do_scsi_device_read_write(char *device) -{ - int skip = 0; - int seek = 0; - int bs = 0; - int ibs = 0; - int obs = 0; - int bpt = DEF_BLOCKS_PER_TRANSFER; - char inf[INOUTF_SZ]; - int in_type = FT_OTHER; - char outf[INOUTF_SZ]; - int out_type = FT_OTHER; - int dio = 0; - int dio_incomplete = 0; - int do_time = 1; - int do_odir = 1; - int scsi_cdbsz = DEF_SCSI_CDBSZ; - int fua_mode = 0; - int do_sync = 1; - int do_blk_sgio = 1; - int do_append = 1; - int res, t, buf_sz, dio_tmp; - int infd, outfd, blocks; - unsigned char *wrkBuff; - unsigned char *wrkPos; - int in_num_sect = 0; - int out_num_sect = 0; - int in_sect_sz, out_sect_sz; - char ebuff[EBUFF_SZ]; - int blocks_per; - int req_count; - struct timeval start_tm, end_tm; - - print_msg(TEST_BREAK, __FUNCTION__); - strcpy(inf, "/dev/zero"); - strcpy(outf, device); - - if (bs <= 0) { - bs = DEF_BLOCK_SIZE; - fprintf(stderr, - "Assume default 'bs' (block size) of %d bytes\n", bs); - } - if ((ibs && (ibs != bs)) || (obs && (obs != bs))) { - fprintf(stderr, - "If 'ibs' or 'obs' given must be same as 'bs'\n"); - usage(); - return 1; - } - if ((skip < 0) || (seek < 0)) { - fprintf(stderr, "skip and seek cannot be negative\n"); - return 1; - } - if ((do_append > 0) && (seek > 0)) { - fprintf(stderr, "Can't use both append and seek switches\n"); - return 1; - } -#ifdef SG_DEBUG - fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n", - inf, skip, outf, seek, dd_count); -#endif - install_handler(SIGINT, interrupt_handler); - install_handler(SIGQUIT, interrupt_handler); - install_handler(SIGPIPE, interrupt_handler); - install_handler(SIGUSR1, siginfo_handler); - - infd = STDIN_FILENO; - outfd = STDOUT_FILENO; - if (inf[0] && ('-' != inf[0])) { - in_type = dd_filetype(inf); - - if ((FT_BLOCK & in_type) && do_blk_sgio) - in_type |= FT_SG; - - if (FT_ST == in_type) { - fprintf(stderr, - ME "unable to use scsi tape device %s\n", inf); - return 1; - } else if (FT_SG & in_type) { - if ((infd = open(inf, O_RDWR)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for sg reading", - inf); - perror(ebuff); - return 1; - } - t = bs * bpt; - res = ioctl(infd, SG_SET_RESERVED_SIZE, &t); - if (res < 0) - perror(ME "SG_SET_RESERVED_SIZE error"); - res = ioctl(infd, SG_GET_VERSION_NUM, &t); - if ((res < 0) || (t < 30000)) { - if (FT_BLOCK & in_type) - fprintf(stderr, - ME - "SG_IO unsupported on this block" - " device\n"); - else - fprintf(stderr, - ME - "sg driver prior to 3.x.y\n"); - return 1; - } - } else { - if (do_odir && (FT_BLOCK == in_type)) - infd = open(inf, O_RDONLY | O_DIRECT); - else - infd = open(inf, O_RDONLY); - if (infd < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for reading", - inf); - perror(ebuff); - return 1; - } else if (skip > 0) { - llse_loff_t offset = skip; - - offset *= bs; /* could exceed 32 bits here! */ - if (llse_llseek(infd, offset, SEEK_SET) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "couldn't skip to required position on %s", - inf); - perror(ebuff); - return 1; - } - } - } - } - - if (outf[0] && ('-' != outf[0])) { - out_type = dd_filetype(outf); - - if ((FT_BLOCK & out_type) && do_blk_sgio) - out_type |= FT_SG; - - if (FT_ST == out_type) { - fprintf(stderr, - ME "unable to use scsi tape device %s\n", outf); - return 1; - } else if (FT_SG & out_type) { - if ((outfd = open(outf, O_RDWR)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for sg writing", - outf); - perror(ebuff); - return 1; - } - t = bs * bpt; - res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t); - if (res < 0) - perror(ME "SG_SET_RESERVED_SIZE error"); - res = ioctl(outfd, SG_GET_VERSION_NUM, &t); - if ((res < 0) || (t < 30000)) { - fprintf(stderr, - ME "sg driver prior to 3.x.y\n"); - return 1; - } - } else if (FT_DEV_NULL & out_type) - outfd = -1; /* don't bother opening */ - else { - if (FT_RAW != out_type) { - int flags = O_WRONLY | O_CREAT; - - if (do_odir && (FT_BLOCK == out_type)) - flags |= O_DIRECT; - else if (do_append) - flags |= O_APPEND; - if ((outfd = open(outf, flags, 0666)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "could not open %s for writing", - outf); - perror(ebuff); - return 1; - } - } else { - if ((outfd = open(outf, O_WRONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "could not open %s for raw writing", - outf); - perror(ebuff); - return 1; - } - } - if (seek > 0) { - llse_loff_t offset = seek; - - offset *= bs; /* could exceed 32 bits here! */ - if (llse_llseek(outfd, offset, SEEK_SET) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "couldn't seek to required position on %s", - outf); - perror(ebuff); - return 1; - } - } - } - } - if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) { - fprintf(stderr, - "Can't have both 'if' as stdin _and_ 'of' as stdout\n"); - return 1; - } - - if (dd_count < 0) { - if (FT_SG & in_type) { - res = read_capacity(infd, &in_num_sect, &in_sect_sz); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(in), continuing\n"); - res = - read_capacity(infd, &in_num_sect, - &in_sect_sz); - } - if (0 != res) { - fprintf(stderr, - "Unable to read capacity on %s\n", inf); - in_num_sect = -1; - } else { - if (in_num_sect > skip) - in_num_sect -= skip; - } - } - if (FT_SG & out_type) { - res = read_capacity(outfd, &out_num_sect, &out_sect_sz); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(out), continuing\n"); - res = - read_capacity(outfd, &out_num_sect, - &out_sect_sz); - } - if (0 != res) { - fprintf(stderr, - "Unable to read capacity on %s\n", - outf); - out_num_sect = -1; - } else { - if (out_num_sect > seek) - out_num_sect -= seek; - } - } -#ifdef SG_DEBUG - fprintf(stderr, - "Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n", - dd_count, in_num_sect, out_num_sect); -#endif - if (in_num_sect > 0) { - if (out_num_sect > 0) - dd_count = - (in_num_sect > - out_num_sect) ? out_num_sect : in_num_sect; - else - dd_count = in_num_sect; - } else - dd_count = out_num_sect; - } - if (dd_count < 0) { - fprintf(stderr, "Couldn't calculate count, please give one\n"); - return 1; - } - - if (dio || do_odir || (FT_RAW == in_type) || (FT_RAW == out_type)) { - size_t psz = getpagesize(); - wrkBuff = malloc(bs * bpt + psz); - if (0 == wrkBuff) { - fprintf(stderr, "Not enough user memory for raw\n"); - return 1; - } - wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) & - (~(psz - 1))); - } else { - wrkBuff = malloc(bs * bpt); - if (0 == wrkBuff) { - fprintf(stderr, "Not enough user memory\n"); - return 1; - } - wrkPos = wrkBuff; - } - - blocks_per = bpt; -#ifdef SG_DEBUG - fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n", - dd_count, blocks_per); -#endif - if (do_time) { - start_tm.tv_sec = 0; - start_tm.tv_usec = 0; - gettimeofday(&start_tm, NULL); - } - req_count = dd_count; - - while (dd_count > 0) { - blocks = (dd_count > blocks_per) ? blocks_per : dd_count; - if (FT_SG & in_type) { - int fua = fua_mode & 2; - - dio_tmp = dio; - res = - sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, - fua, &dio_tmp); - if (1 == res) { /* ENOMEM, find what's available+try that */ - if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < - 0) { - perror("RESERVED_SIZE ioctls failed"); - break; - } - blocks_per = (buf_sz + bs - 1) / bs; - blocks = blocks_per; - fprintf(stderr, - "Reducing read to %d blocks per loop\n", - blocks_per); - res = - sg_read(infd, wrkPos, blocks, skip, bs, - scsi_cdbsz, fua, &dio_tmp); - } else if (2 == res) { - fprintf(stderr, - "Unit attention, media changed, continuing (r)\n"); - res = - sg_read(infd, wrkPos, blocks, skip, bs, - scsi_cdbsz, fua, &dio_tmp); - } - if (0 != res) { - fprintf(stderr, "sg_read failed, skip=%d\n", - skip); - break; - } else { - in_full += blocks; - if (dio && (0 == dio_tmp)) - dio_incomplete++; - } - } else { - while (((res = read(infd, wrkPos, blocks * bs)) < 0) && - (EINTR == errno)) ; - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "reading, skip=%d ", skip); - perror(ebuff); - break; - } else if (res < blocks * bs) { - dd_count = 0; - blocks = res / bs; - if ((res % bs) > 0) { - blocks++; - in_partial++; - } - } - in_full += blocks; - } - - if (FT_SG & out_type) { - int fua = fua_mode & 1; - - dio_tmp = dio; - res = - sg_write(outfd, wrkPos, blocks, seek, bs, - scsi_cdbsz, fua, &dio_tmp); - if (1 == res) { /* ENOMEM, find what's available+try that */ - if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz) - < 0) { - perror("RESERVED_SIZE ioctls failed"); - break; - } - blocks_per = (buf_sz + bs - 1) / bs; - blocks = blocks_per; - fprintf(stderr, - "Reducing write to %d blocks per loop\n", - blocks); - res = - sg_write(outfd, wrkPos, blocks, seek, bs, - scsi_cdbsz, fua, &dio_tmp); - } else if (2 == res) { - fprintf(stderr, - "Unit attention, media changed, continuing (w)\n"); - res = - sg_write(outfd, wrkPos, blocks, seek, bs, - scsi_cdbsz, fua, &dio_tmp); - } else if (0 != res) { - fprintf(stderr, "sg_write failed, seek=%d\n", - seek); - break; - } else { - out_full += blocks; - if (dio && (0 == dio_tmp)) - dio_incomplete++; - } - } else if (FT_DEV_NULL & out_type) - out_full += blocks; /* act as if written out without error */ - else { - while (((res = write(outfd, wrkPos, blocks * bs)) < 0) - && (EINTR == errno)) ; - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "writing, seek=%d ", seek); - perror(ebuff); - break; - } else if (res < blocks * bs) { - fprintf(stderr, - "output file probably full, seek=%d ", - seek); - blocks = res / bs; - out_full += blocks; - if ((res % bs) > 0) - out_partial++; - break; - } else - out_full += blocks; - } - if (dd_count > 0) - dd_count -= blocks; - skip += blocks; - seek += blocks; - } - if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { - struct timeval res_tm; - double a, b; - - gettimeofday(&end_tm, NULL); - res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; - res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; - if (res_tm.tv_usec < 0) { - --res_tm.tv_sec; - res_tm.tv_usec += 1000000; - } - a = res_tm.tv_sec; - a += (0.000001 * res_tm.tv_usec); - b = (double)bs *(req_count - dd_count); - printf("time to transfer data was %d.%06d secs", - (int)res_tm.tv_sec, (int)res_tm.tv_usec); - if ((a > 0.00001) && (b > 511)) - printf(", %.2f MB/sec\n", b / (a * 1000000.0)); - else - printf("\n"); - } - if (do_sync) { - if (FT_SG & out_type) { - fprintf(stderr, ">> Synchronizing cache on %s\n", outf); - res = sync_cache(outfd); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(in), continuing\n"); - res = sync_cache(outfd); - } - if (0 != res) - fprintf(stderr, - "Unable to synchronize cache\n"); - } - } - free(wrkBuff); - if (STDIN_FILENO != infd) - close(infd); - if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type)) - close(outfd); - res = 0; - if (0 != dd_count) { - fprintf(stderr, "Some error occurred,"); - res = 2; - } - print_stats(); - if (dio_incomplete) { - int fd; - char c; - - fprintf(stderr, - ">> Direct IO requested but incomplete %d times\n", - dio_incomplete); - if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { - if (1 == read(fd, &c, 1)) { - if ('0' == c) - fprintf(stderr, - ">>> %s set to '0' but should be set " - "to '1' for direct IO\n", - proc_allow_dio); - } - close(fd); - } - } - if (sum_of_resids) - fprintf(stderr, ">> Non-zero sum of residual counts=%d\n", - sum_of_resids); - return res; -} - -/* Returns 0 when successful, else -1 */ -static int do_scsi_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, - void *resp, int mx_resp_len, int noisy) -{ - int res; - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = - { INQUIRY_CMD, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - if (cmddt) - inqCmdBlk[1] |= 2; - if (evpd) - inqCmdBlk[1] |= 1; - inqCmdBlk[2] = (unsigned char)pg_op; - inqCmdBlk[4] = (unsigned char)mx_resp_len; - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(inqCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = inqCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (inquiry) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - if (noisy) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, "Inquiry error, CmdDt=%d, " - "EVPD=%d, page_opcode=%x ", cmddt, evpd, - pg_op); - sg_chk_n_print3(ebuff, &io_hdr); - } - return -1; - } -} - -int do_scsi_inquiry(char *device, int hex_flag) -{ - int sg_fd, k, j, num, len, act_len; - int support_num; - char *file_name = 0; - char buff[MX_ALLOC_LEN + 1]; - unsigned char rsp_buff[MX_ALLOC_LEN + 1]; - unsigned int num_opcode = 0; - int do_evpd = 0; - int do_cmddt = 0; - int do_cmdlst = 0; - int do_hex = 0; - int do_raw = 0; - int do_pci = 0; - int do_36 = 0; - int oflags = O_RDONLY | O_NONBLOCK; - int ansi_version = 0; - int ret = 0; - - file_name = device; - - if (hex_flag) { - do_hex = TRUE; - print_msg(TEST_BREAK, __FUNCTION__); - } else { - do_pci = TRUE; - } - - if (do_pci) - oflags = O_RDWR | O_NONBLOCK; - if ((sg_fd = open(file_name, oflags)) < 0) { - snprintf(ebuff, EBUFF_SZ, "sg_inq: error opening file: %s", - file_name); - perror(ebuff); - return 1; - } - /* Just to be safe, check we have a new sg device by trying an ioctl */ - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { - fprintf(stderr, - "sg_inq: %s doesn't seem to be a version 3 sg device\n", - file_name); - close(sg_fd); - return 1; - } - memset(rsp_buff, 0, MX_ALLOC_LEN + 1); - - if (!(do_cmddt || do_evpd)) { - if (!do_raw) - printf("standard INQUIRY:\n"); - if (num_opcode > 0) - printf - (" <>\n"); - - if (0 == do_scsi_inq(sg_fd, 0, 0, 0, rsp_buff, 36, 1)) { - len = rsp_buff[4] + 5; - ansi_version = rsp_buff[2] & 0x7; - if ((len > 36) && (len < 256) && (!do_36)) { - if (do_scsi_inq - (sg_fd, 0, 0, 0, rsp_buff, len, 1)) { - fprintf(stderr, - "second INQUIRY (%d byte) failed\n", - len); - return 1; - } - if (len != (rsp_buff[4] + 5)) { - fprintf(stderr, - "strange, twin INQUIRYs yield different " - "'additional length'\n"); - ret = 2; - } - } - if (do_36) { - act_len = len; - len = 36; - } else - act_len = len; - if (do_hex) - dStrHex((const char *)rsp_buff, len, 0); - else { - printf - (" PQual=%d, Device type=%d, RMB=%d, ANSI version=%d, ", - (rsp_buff[0] & 0xe0) >> 5, - rsp_buff[0] & 0x1f, - ! !(rsp_buff[1] & 0x80), ansi_version); - printf("[full version=0x%02x]\n", - (unsigned int)rsp_buff[2]); - printf - (" AERC=%d, TrmTsk=%d, NormACA=%d, HiSUP=%d, " - "Resp data format=%d, SCCS=%d\n", - ! !(rsp_buff[3] & 0x80), - ! !(rsp_buff[3] & 0x40), - ! !(rsp_buff[3] & 0x20), - ! !(rsp_buff[3] & 0x10), - rsp_buff[3] & 0x0f, - ! !(rsp_buff[5] & 0x80)); - printf - (" BQue=%d, EncServ=%d, MultiP=%d, MChngr=%d, " - "ACKREQQ=%d, ", ! !(rsp_buff[6] & 0x80), - ! !(rsp_buff[6] & 0x40), - ! !(rsp_buff[6] & 0x10), - ! !(rsp_buff[6] & 0x08), - ! !(rsp_buff[6] & 0x04)); - printf("Addr16=%d\n RelAdr=%d, ", - ! !(rsp_buff[6] & 0x01), - ! !(rsp_buff[7] & 0x80)); - printf - ("WBus16=%d, Sync=%d, Linked=%d, TranDis=%d, ", - ! !(rsp_buff[7] & 0x20), - ! !(rsp_buff[7] & 0x10), - ! !(rsp_buff[7] & 0x08), - ! !(rsp_buff[7] & 0x04)); - printf("CmdQue=%d\n", ! !(rsp_buff[7] & 0x02)); - if (len > 56) - printf - (" Clocking=0x%x, QAS=%d, IUS=%d\n", - (rsp_buff[56] & 0x0c) >> 2, - ! !(rsp_buff[56] & 0x2), - ! !(rsp_buff[56] & 0x1)); - if (act_len == len) - printf(" length=%d (0x%x)", len, - len); - else - printf - (" length=%d (0x%x), but only read 36 bytes", - len, len); - if ((ansi_version >= 2) && (len < 36)) - printf - (" [for SCSI>=2, len>=36 is expected]\n"); - else - printf("\n"); - - if (len <= 8) - printf - (" Inquiry response length=%d\n, no vendor, " - "product or revision data\n", len); - else { - if (len < 36) - rsp_buff[len] = '\0'; - memcpy(buff, &rsp_buff[8], 8); - buff[8] = '\0'; - printf(" Vendor identification: %s\n", - buff); - if (len <= 16) - printf - (" Product identification: \n"); - else { - memcpy(buff, &rsp_buff[16], 16); - buff[16] = '\0'; - printf - (" Product identification: %s\n", - buff); - } - if (len <= 32) - printf - (" Product revision level: \n"); - else { - memcpy(buff, &rsp_buff[32], 4); - buff[4] = '\0'; - printf - (" Product revision level: %s\n", - buff); - } - } - } - if (!do_raw && - (0 == - do_scsi_inq(sg_fd, 0, 1, 0x80, rsp_buff, - MX_ALLOC_LEN, 0))) { - len = rsp_buff[3]; - if (len > 0) { - memcpy(buff, rsp_buff + 4, len); - buff[len] = '\0'; - printf(" Product serial number: %s\n", - buff); - } - } - } else { - printf("36 byte INQUIRY failed\n"); - return 1; - } - } else if (do_cmddt) { - int reserved_cmddt; - char op_name[128]; - - if (do_cmdlst) { - printf("Supported command list:\n"); - for (k = 0; k < 256; ++k) { - if (0 == - do_scsi_inq(sg_fd, 1, 0, k, rsp_buff, - MX_ALLOC_LEN, 1)) { - support_num = rsp_buff[1] & 7; - reserved_cmddt = rsp_buff[4]; - if ((3 == support_num) - || (5 == support_num)) { - num = rsp_buff[5]; - for (j = 0; j < num; ++j) - printf(" %.2x", - (int)rsp_buff[6 + - j]); - if (5 == support_num) - printf - (" [vendor specific manner (5)]"); - sg_get_command_name((unsigned - char)k, - sizeof - (op_name) - - 1, op_name); - op_name[sizeof(op_name) - 1] = - '\0'; - printf(" %s\n", op_name); - } else if ((4 == support_num) - || (6 == support_num)) - printf - (" opcode=0x%.2x vendor specific (%d)\n", - k, support_num); - else if ((0 == support_num) - && (reserved_cmddt > 0)) { - printf - (" opcode=0x%.2x ignored cmddt bit, " - "given standard INQUIRY response, stop\n", - k); - break; - } - } else { - fprintf(stderr, - "CmdDt INQUIRY on opcode=0x%.2x: failed\n", - k); - break; - } - } - } else { - if (!do_raw) { - printf("CmdDt INQUIRY, opcode=0x%.2x: [", - num_opcode); - sg_get_command_name((unsigned char)num_opcode, - sizeof(op_name) - 1, - op_name); - op_name[sizeof(op_name) - 1] = '\0'; - printf("%s]\n", op_name); - } - if (0 == do_scsi_inq(sg_fd, 1, 0, num_opcode, rsp_buff, - MX_ALLOC_LEN, 1)) { - len = rsp_buff[5] + 6; - reserved_cmddt = rsp_buff[4]; - if (do_hex) - dStrHex((const char *)rsp_buff, len, 0); - else { - const char *desc_p; - int prnt_cmd = 0; - - support_num = rsp_buff[1] & 7; - num = rsp_buff[5]; - switch (support_num) { - case 0: - if (0 == reserved_cmddt) - desc_p = - "no data available"; - else - desc_p = - "ignored cmddt bit, standard INQUIRY " - "response"; - break; - case 1: - desc_p = "not supported"; - break; - case 2: - desc_p = "reserved (2)"; - break; - case 3: - desc_p = - "supported as per standard"; - prnt_cmd = 1; - break; - case 4: - desc_p = "vendor specific (4)"; - break; - case 5: - desc_p = - "supported in vendor specific way"; - prnt_cmd = 1; - break; - case 6: - desc_p = "vendor specific (6)"; - break; - case 7: - desc_p = "reserved (7)"; - break; - default: - desc_p = "impossible value > 7"; - break; - } - if (prnt_cmd) { - printf(" Support field: %s [", - desc_p); - for (j = 0; j < num; ++j) - printf(" %.2x", - (int)rsp_buff[6 + - j]); - printf(" ]\n"); - } else - printf(" Support field: %s\n", - desc_p); - } - } else { - fprintf(stderr, - "CmdDt INQUIRY on opcode=0x%.2x: failed\n", - num_opcode); - return 1; - } - - } - } else if (do_evpd) { - if (!do_raw) - printf("EVPD INQUIRY, page code=0x%.2x:\n", num_opcode); - if (0 == - do_scsi_inq(sg_fd, 0, 1, num_opcode, rsp_buff, MX_ALLOC_LEN, - 1)) { - len = rsp_buff[3] + 4; - if (num_opcode != rsp_buff[1]) - printf - ("non evpd respone; probably a STANDARD INQUIRY " - "response\n"); - else { - if (!do_hex) - printf(" Only hex output supported\n"); - dStrHex((const char *)rsp_buff, len, 0); - } - } else { - fprintf(stderr, - "EVPD INQUIRY, page code=0x%.2x: failed\n", - num_opcode); - return 1; - } - } - - if (do_pci) { - unsigned char slot_name[16]; - - printf("\n"); - memset(slot_name, '\0', sizeof(slot_name)); - if (ioctl(sg_fd, SCSI_IOCTL_GET_PCI, slot_name) < 0) { - if (EINVAL == errno) - printf - ("ioctl(SCSI_IOCTL_GET_PCI) not supported by this " - "kernel\n"); - else if (ENXIO == errno) - printf - ("associated adapter not a PCI device?\n"); - else - perror("ioctl(SCSI_IOCTL_GET_PCI) failed"); - } else - printf("PCI:slot_name: %s\n", slot_name); - } - - close(sg_fd); - return ret; -} - -int show_scsi_maps() -{ - int sg_fd, res, k; - int do_numeric = NUMERIC_SCAN_DEF; - int do_all_s = 1; - int do_sd = 0; - int do_st = 0; - int do_osst = 0; - int do_sr = 0; - int do_scd = 0; - int do_extra = 1; - int do_inquiry = 0; - char fname[64]; - int num_errors = 0; - int num_silent = 0; - int eacces_err = 0; - int last_sg_ind = -1; - struct stat stat_buf; - - print_msg(TEST_BREAK, __FUNCTION__); - - if (stat(devfs_id, &stat_buf) == 0) - printf("# Note: the devfs pseudo file system is present\n"); - - for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS); - ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) { - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname); - perror("sg_map: close error"); - return 1; - } - make_dev_name(fname, "/dev/sg", k, do_numeric); - - sg_fd = open(fname, O_RDONLY | O_NONBLOCK); - if (sg_fd < 0) { - if (EBUSY == errno) { - map_arr[k].active = -2; - continue; - } else if ((ENODEV == errno) || (ENOENT == errno) || - (ENXIO == errno)) { - ++num_errors; - ++num_silent; - map_arr[k].active = -1; - continue; - } else { - if (EACCES == errno) - eacces_err = 1; - snprintf(ebuff, EBUFF_SZ, "Error opening %s ", - fname); - perror(ebuff); - ++num_errors; - continue; - } - } - res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - "device %s failed on sg ioctl, skip", fname); - perror(ebuff); - ++num_errors; - continue; - } - if (do_inquiry) { - char buff[36]; - - if (0 == - do_scsi_inq(sg_fd, 0, 0, 0, buff, sizeof(buff), - 1)) { - memcpy(map_arr[k].vendor, &buff[8], 8); - memcpy(map_arr[k].product, &buff[16], 16); - memcpy(map_arr[k].revision, &buff[32], 4); - } - } - map_arr[k].active = 1; - map_arr[k].oth_dev_num = -1; - last_sg_ind = k; - } - if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) { - printf("Stopping because there are too many error\n"); - if (eacces_err) - printf(" root access may be required\n"); - return 1; - } - if (last_sg_ind < 0) { - printf("Stopping because no sg devices found\n"); - } - - if (do_all_s || do_sd) - scan_dev_type("/dev/sd", MAX_SD_DEVS, 0, LIN_DEV_TYPE_SD, - last_sg_ind); - if (do_all_s || do_sr) - scan_dev_type("/dev/sr", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SR, - last_sg_ind); - if (do_all_s || do_scd) - scan_dev_type("/dev/scd", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SCD, - last_sg_ind); - if (do_all_s || do_st) - scan_dev_type("/dev/st", MAX_ST_DEVS, 1, LIN_DEV_TYPE_ST, - last_sg_ind); - if (do_all_s || do_osst) - scan_dev_type("/dev/osst", MAX_OSST_DEVS, 1, LIN_DEV_TYPE_OSST, - last_sg_ind); - - for (k = 0; k <= last_sg_ind; ++k) { - make_dev_name(fname, "/dev/sg", k, do_numeric); - printf("%s", fname); - switch (map_arr[k].active) { - case -2: - printf(do_extra ? " -2 -2 -2 -2 -2" : " busy"); - break; - case -1: - printf(do_extra ? " -1 -1 -1 -1 -1" : - " not present"); - break; - case 0: - printf(do_extra ? " -3 -3 -3 -3 -3" : - " some error\n"); - break; - case 1: - if (do_extra) - printf(" %d %d %d %d %d", - map_arr[k].sg_dat.host_no, - map_arr[k].sg_dat.channel, - map_arr[k].sg_dat.scsi_id, - map_arr[k].sg_dat.lun, - map_arr[k].sg_dat.scsi_type); - switch (map_arr[k].lin_dev_type) { - case LIN_DEV_TYPE_SD: - make_dev_name(fname, "/dev/sd", - map_arr[k].oth_dev_num, 0); - printf(" %s", fname); - break; - case LIN_DEV_TYPE_ST: - make_dev_name(fname, "/dev/st", - map_arr[k].oth_dev_num, 1); - printf(" %s", fname); - break; - case LIN_DEV_TYPE_OSST: - make_dev_name(fname, "/dev/osst", - map_arr[k].oth_dev_num, 1); - printf(" %s", fname); - break; - case LIN_DEV_TYPE_SR: - make_dev_name(fname, "/dev/sr", - map_arr[k].oth_dev_num, 1); - printf(" %s", fname); - break; - case LIN_DEV_TYPE_SCD: - make_dev_name(fname, "/dev/scd", - map_arr[k].oth_dev_num, 1); - printf(" %s", fname); - break; - default: - break; - } - if (do_inquiry) - printf(" %.8s %.16s %.4s", map_arr[k].vendor, - map_arr[k].product, map_arr[k].revision); - break; - default: - printf(" bad logic\n"); - break; - } - printf("\n"); - } - return 0; -} - -static int find_dev_in_sg_arr(My_scsi_idlun * my_idlun, int host_no, - int last_sg_ind) -{ - int k; - struct sg_scsi_id *sidp; - - for (k = 0; k <= last_sg_ind; ++k) { - sidp = &(map_arr[k].sg_dat); - if ((host_no == sidp->host_no) && - ((my_idlun->dev_id & 0xff) == sidp->scsi_id) && - (((my_idlun->dev_id >> 8) & 0xff) == sidp->lun) && - (((my_idlun->dev_id >> 16) & 0xff) == sidp->channel)) - return k; - } - return -1; -} - -static void scan_dev_type(const char *leadin, int max_dev, int do_numeric, - int lin_dev_type, int last_sg_ind) -{ - int k, res, ind, sg_fd = 0; - int num_errors = 0; - int num_silent = 0; - int host_no = -1; - int nonMappedDevicesPresent = FALSE; - My_scsi_idlun my_idlun; - char fname[64]; - - for (k = 0, res = 0; (k < max_dev) && (num_errors < MAX_ERRORS); - ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) { - -/* ignore close() errors */ -#if 0 - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname); - perror("sg_map: close error"); -#ifndef IGN_CLOSE_ERR - return; -#else - ++num_errors; - sg_fd = 0; -#endif - } -#endif - make_dev_name(fname, leadin, k, do_numeric); -#ifdef DEBUG - printf("Trying %s: ", fname); -#endif - - sg_fd = open(fname, O_RDONLY | O_NONBLOCK); - if (sg_fd < 0) { -#ifdef DEBUG - printf("ERROR %i\n", errno); -#endif - if (EBUSY == errno) { - printf("Device %s is busy\n", fname); - ++num_errors; - continue; - } else if ((ENODEV == errno) || (ENOENT == errno) || - (ENXIO == errno)) { - ++num_errors; - ++num_silent; - continue; - } else { - snprintf(ebuff, EBUFF_SZ, "Error opening %s ", - fname); - perror(ebuff); - ++num_errors; - continue; - } - } - - res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - "device %s failed on scsi ioctl(idlun), skip", - fname); - perror(ebuff); - ++num_errors; -#ifdef DEBUG - printf("Couldn't get IDLUN!\n"); -#endif - continue; - } - res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no); - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - "device %s failed on scsi ioctl(bus_number), skip", - fname); - perror(ebuff); - ++num_errors; -#ifdef DEBUG - printf("Couldn't get BUS!\n"); -#endif - continue; - } -#ifdef DEBUG - printf("%i(%x) %i %i %i %i\n", host_no, my_idlun.host_unique_id, - (my_idlun.dev_id >> 24) & 0xff, - (my_idlun.dev_id >> 16) & 0xff, - (my_idlun.dev_id >> 8) & 0xff, my_idlun.dev_id & 0xff); -#endif - ind = find_dev_in_sg_arr(&my_idlun, host_no, last_sg_ind); - if (ind >= 0) { - map_arr[ind].oth_dev_num = k; - map_arr[ind].lin_dev_type = lin_dev_type; - } else if (ind != -1) { - printf - ("Strange, could not find device %s mapped to sg device error %d??\n", - fname, ind); - } else { - nonMappedDevicesPresent = TRUE; - } - } - if (nonMappedDevicesPresent) { - printf("Unmapped Devices found...\n\n"); - } -} - -/* Returns 0 when successful, else -1 */ -static int do_simple_inq(int sg_fd, void *resp, int mx_resp_len, int noisy) -{ - int res; - unsigned char inqCmdBlk[INQUIRY_CMDLEN] = - { INQUIRY_CMD, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - inqCmdBlk[4] = (unsigned char)mx_resp_len; - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(inqCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = inqCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (inquiry) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - if (noisy) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, "Inquiry error "); - sg_chk_n_print3(ebuff, &io_hdr); - } - return -1; - } -} - -static int do_modes(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code, - void *resp, int mx_resp_len, int noisy, int mode6) -{ - int res; - unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = - { MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0); - modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); - modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff); - if (mx_resp_len > (mode6 ? 0xff : 0xffff)) { - printf(ME "mx_resp_len too big\n"); - return -1; - } - if (mode6) { - modesCmdBlk[0] = MODE_SENSE6_CMD; - modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); - } else { - modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); - modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); - } - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - memset(sense_b, 0, sizeof(sense_b)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN; - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = modesCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (mode sense) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - if (noisy) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d " - "pc=%d page_code=%x sub_page_code=%x\n ", - dbd, pc, pg_code, sub_pg_code); - sg_chk_n_print3(ebuff, &io_hdr); - } - if ((0x70 == (0x7f & sense_b[0])) && (0x20 == sense_b[12]) && - (0x0 == sense_b[13])) { - if (mode6) - fprintf(stderr, - ">>>>>> drop '-6' switch and try again with " - "a 10 byte MODE SENSE\n"); - else - fprintf(stderr, - ">>>>>> add '-6' switch and try again with " - "a 6 byte MODE SENSE\n"); - } - return -1; - } -} - -const char *scsi_ptype_strs[] = { - "disk", - "tape", - "printer", - "processor", - "write once optical disk", - "cd/dvd", - "scanner", - "optical memory device", - "medium changer", - "communications", - "graphics", - "graphics", - "storage array controller", - "enclosure services device", - "simplified direct access device", - "optical card reader/writer device", -}; - -const char *get_ptype_str(int scsi_ptype) -{ - int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]); - - return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : ""; -} - -static struct page_code_desc pc_desc_all[] = { - {0x0, "Unit Attention condition [vendor: page format not required]"}, - {0x2, "Disconnect-Reconnect"}, - {0xa, "Control"}, - {0x15, "Extended"}, - {0x16, "Extended device-type specific"}, - {0x18, "Protocol specific LUN"}, - {0x19, "Protocol specific port"}, - {0x1a, "Power condition"}, - {0x1c, "Informational exceptions control"}, - {0x3f, "[yields all supported pages]"}, -}; - -static struct page_code_desc pc_desc_disk[] = { - {0x1, "Read-Write error recovery"}, - {0x3, "Format"}, - {0x4, "Rigid disk geometry"}, - {0x5, "Flexible geometry"}, - {0x7, "Verify error recovery"}, - {0x8, "Caching"}, - {0x9, "Peripheral device (spc-2 ?)"}, - {0xb, "Medium types supported"}, - {0xc, "Notch and partition"}, - {0xd, "Power condition (obsolete)"}, - {0x10, "XOR control"}, -}; - -static struct page_code_desc pc_desc_tape[] = { - {0xf, "Data Compression"}, - {0x10, "Device config"}, - {0x11, "Medium Partition [1]"}, - {0x12, "Medium Partition [2]"}, - {0x13, "Medium Partition [3]"}, - {0x14, "Medium Partition [4]"}, - {0x1c, "Informational exceptions control (tape version)"}, -}; - -static struct page_code_desc pc_desc_cddvd[] = { - {0x1, "Read-Write error recovery"}, - {0x3, "MRW"}, - {0x5, "Write parameters"}, - {0xd, "CD device parameters (obsolete)"}, - {0xe, "CD audio"}, - {0x1a, "Power condition"}, - {0x1c, "Fault/failure reporting control"}, - {0x1d, "Timeout and protect"}, - {0x2a, "MM capabilities and mechanical status (obsolete)"}, -}; - -static struct page_code_desc pc_desc_smc[] = { - {0x1d, "Element address assignment"}, - {0x1e, "Transport geometry parameters"}, - {0x1f, "Device capabilities"}, -}; - -static struct page_code_desc pc_desc_scc[] = { - {0x1b, "LUN mapping"}, -}; - -static struct page_code_desc pc_desc_ses[] = { - {0x14, "Enclosure services management"}, -}; - -struct page_code_desc *find_mode_page_table(int scsi_ptype, int *size) -{ - switch (scsi_ptype) { - case 0: /* disk (direct access) type devices */ - case 4: - case 7: - case 0xe: - *size = sizeof(pc_desc_disk) / sizeof(pc_desc_disk[0]); - return &pc_desc_disk[0]; - case 1: /* tape devices */ - case 2: - *size = sizeof(pc_desc_tape) / sizeof(pc_desc_tape[0]); - return &pc_desc_tape[0]; - case 5: /* cd/dvd devices */ - *size = sizeof(pc_desc_cddvd) / sizeof(pc_desc_cddvd[0]); - return &pc_desc_cddvd[0]; - case 8: /* medium changer devices */ - *size = sizeof(pc_desc_smc) / sizeof(pc_desc_smc[0]); - return &pc_desc_smc[0]; - case 0xc: /* storage array devices */ - *size = sizeof(pc_desc_scc) / sizeof(pc_desc_scc[0]); - return &pc_desc_scc[0]; - case 0xd: /* enclosure services devices */ - *size = sizeof(pc_desc_ses) / sizeof(pc_desc_ses[0]); - return &pc_desc_ses[0]; - } - *size = 0; - return NULL; -} - -const char *find_page_code_desc(int page_num, int scsi_ptype) -{ - int k; - int num; - const struct page_code_desc *pcdp; - - pcdp = find_mode_page_table(scsi_ptype, &num); - if (pcdp) { - for (k = 0; k < num; ++k, ++pcdp) { - if (page_num == pcdp->page_code) - return pcdp->desc; - else if (page_num < pcdp->page_code) - break; - } - } - pcdp = &pc_desc_all[0]; - num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]); - for (k = 0; k < num; ++k, ++pcdp) { - if (page_num == pcdp->page_code) - return pcdp->desc; - else if (page_num < pcdp->page_code) - break; - } - return NULL; -} - -static void list_page_codes(int scsi_ptype) -{ - int k; - int num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]); - const struct page_code_desc *pcdp = &pc_desc_all[0]; - int num_ptype; - const struct page_code_desc *pcd_ptypep; - - pcd_ptypep = find_mode_page_table(scsi_ptype, &num_ptype); - printf("Page_Code Description\n"); - for (k = 0; k < 0x3f; ++k) { - if (pcd_ptypep && (num_ptype > 0)) { - if (k == pcd_ptypep->page_code) { - printf(" 0x%02x %s\n", - pcd_ptypep->page_code, pcd_ptypep->desc); - ++pcd_ptypep; - --num_ptype; - continue; - } else if (k > pcd_ptypep->page_code) { - pcd_ptypep++; - --num_ptype; - } - } - if (pcdp && (num > 0)) { - if (k == pcdp->page_code) { - printf(" 0x%02x %s\n", pcdp->page_code, - pcdp->desc); - ++pcdp; - --num; - continue; - } else if (k > pcdp->page_code) { - pcdp++; - --num; - } - } - } -} - -int show_scsi_modes(char *device) -{ - int sg_fd, k, num, len, md_len, bd_len, longlba, page_num; - char *file_name = 0; - char ebuff[EBUFF_SZ]; - const char *descp; - unsigned char rsp_buff[MODE_ALLOC_LEN]; - int rsp_buff_size = MODE_ALLOC_LEN; - int pg_code = 0; - int sub_pg_code = 0; - int pc = 0; - int do_all = 1; - int do_dbd = 0; - int do_hex = 0; - int do_mode6 = 0; /* Use MODE SENSE(6) instead of MODE SENSE(10) */ - int oflags = O_RDONLY | O_NONBLOCK; - struct sg_scsi_id a_sid; - int scsi_ptype, density_code_off; - unsigned char *ucp; - unsigned char uc; - - print_msg(TEST_BREAK, __FUNCTION__); - - file_name = device; - - list_page_codes(0); - - /* The 6 bytes command only allows up to 255 bytes of response data */ - if (do_mode6) - rsp_buff_size = 255; - - if ((sg_fd = open(file_name, oflags)) < 0) { - snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", - file_name); - perror(ebuff); - return 1; - } - /* Just to be safe, check we have a new sg device by trying an ioctl */ - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { - printf(ME "%s doesn't seem to be a version 3 sg device\n", - file_name); - close(sg_fd); - return 1; - } - if (ioctl(sg_fd, SG_GET_SCSI_ID, &a_sid) < 0) { - unsigned char inqBuff[36]; - - if (do_simple_inq(sg_fd, inqBuff, sizeof(inqBuff), 1)) { - printf(ME "%s doesn't respond to a SCSI INQUIRY\n", - file_name); - close(sg_fd); - return 1; - } - scsi_ptype = inqBuff[0] & 0x1f; /* fetch peripheral device type */ - } else - scsi_ptype = a_sid.scsi_type; - printf(" SCSI peripheral type: %s [0x%x] (from INQUIRY)\n", - get_ptype_str(scsi_ptype), scsi_ptype); - - if (do_all) - pg_code = MODE_CODE_ALL; - - if (0 == do_modes(sg_fd, do_dbd, pc, pg_code, sub_pg_code, - rsp_buff, rsp_buff_size, 1, do_mode6)) { - int medium_type, specific, headerlen; - - printf("Mode parameter header from %s byte MODE SENSE:\n", - (do_mode6 ? "6" : "10")); - if (do_mode6) { - headerlen = 4; - if (do_hex) - dStrHex((const char *)rsp_buff, headerlen, 1); - md_len = rsp_buff[0] + 1; - bd_len = rsp_buff[3]; - medium_type = rsp_buff[1]; - specific = rsp_buff[2]; - longlba = 0; /* what is this field? */ - } else { - headerlen = 8; - md_len = (rsp_buff[0] << 8) + rsp_buff[1] + 2; - bd_len = (rsp_buff[6] << 8) + rsp_buff[7]; - medium_type = rsp_buff[2]; - specific = rsp_buff[3]; - longlba = rsp_buff[4] & 1; - } - if (do_hex) - dStrHex((const char *)rsp_buff, headerlen, 1); - printf(" Mode data length=%d, medium type=0x%.2x, specific" - " param=0x%.2x, longlba=%d\n", md_len, medium_type, - specific, longlba); - if (md_len > rsp_buff_size) { - printf - ("Only fetched %d bytes of response, truncate output\n", - rsp_buff_size); - md_len = rsp_buff_size; - if (bd_len + headerlen > rsp_buff_size) - bd_len = rsp_buff_size - headerlen; - } - printf(" Block descriptor length=%d\n", bd_len); - if (bd_len > 0) { - len = 8; - density_code_off = 0; - num = bd_len; - if (longlba) { - printf("> longlba block descriptors:\n"); - len = 16; - density_code_off = 8; - } else if (0 == scsi_ptype) { - printf - ("> Direct access device block descriptors:\n"); - density_code_off = 4; - } else - printf - ("> General mode parameter block descriptors:\n"); - - ucp = rsp_buff + headerlen; - while (num > 0) { - printf(" Density code=0x%x\n", - *(ucp + density_code_off)); - dStrHex((const char *)ucp, len, 1); - ucp += len; - num -= len; - } - printf("\n"); - } - ucp = rsp_buff + bd_len + headerlen; /* start of mode page(s) */ - md_len -= bd_len + headerlen; /* length of mode page(s) */ - while (md_len > 0) { /* got mode page(s) */ - uc = *ucp; - page_num = ucp[0] & 0x3f; - if (do_hex) - descp = NULL; - else { - descp = - find_page_code_desc(page_num, scsi_ptype); - if (NULL == descp) - snprintf(ebuff, EBUFF_SZ, - "vendor[0x%x]", page_num); - } - if (uc & 0x40) { - len = (ucp[2] << 8) + ucp[3] + 4; - if (do_hex) - printf - (">> page_code=0x%x, subpage_code=0x%x, " - "page_control=%d\n", page_num, - ucp[1], pc); - else - printf - (">> page_code: %s, subpage_code=0x%x, " - "page_control: %s\n", - (descp ? descp : ebuff), ucp[1], - pg_control_str_arr[pc]); - } else { - len = ucp[1] + 2; - if (do_hex) - printf - (">> page_code=0x%x, page_control=%d\n", - page_num, pc); - else - printf - (">> page_code: %s, page_control: %s\n", - (descp ? descp : ebuff), - pg_control_str_arr[pc]); - } - dStrHex((const char *)ucp, len, 1); - ucp += len; - md_len -= len; - } - } - - close(sg_fd); - return 0; -} - -int do_scsi_read_buffer(char *device) -{ - int sg_fd, res; - unsigned int k, num; - unsigned char rbCmdBlk[RB_CMD_LEN]; - unsigned char *rbBuff = NULL; - void *rawp = NULL; - unsigned char sense_buffer[32]; - int buf_capacity = 0; - int do_quick = 0; - int do_dio = 0; - int do_mmap = 1; - int do_time = 0; - int buf_size = 0; - unsigned int total_size_mb = RB_MB_TO_READ; - char *file_name = 0; - size_t psz = getpagesize(); - int dio_incomplete = 0; - sg_io_hdr_t io_hdr; - struct timeval start_tm, end_tm; -#ifdef SG_DEBUG - int clear = 1; -#endif - - print_msg(TEST_BREAK, __FUNCTION__); - - file_name = device; - - sg_fd = open(file_name, O_RDONLY); - if (sg_fd < 0) { - perror(ME "open error"); - return 1; - } - /* Don't worry, being very careful not to write to a none-sg file ... */ - res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k); - if ((res < 0) || (k < 30000)) { - printf(ME "not a sg device, or driver prior to 3.x\n"); - return 1; - } - if (do_mmap) { - do_dio = 0; - do_quick = 0; - } - if (NULL == (rawp = malloc(512))) { - printf(ME "out of memory (query)\n"); - return 1; - } - rbBuff = rawp; - - memset(rbCmdBlk, 0, RB_CMD_LEN); - rbCmdBlk[0] = RB_OPCODE; - rbCmdBlk[1] = RB_MODE_DESC; - rbCmdBlk[8] = RB_DESC_LEN; - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(rbCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_buffer); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = RB_DESC_LEN; - io_hdr.dxferp = rbBuff; - io_hdr.cmdp = rbCmdBlk; - io_hdr.sbp = sense_buffer; - io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */ - /* do normal IO to find RB size (not dio or mmap-ed at this stage) */ - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror(ME "SG_IO READ BUFFER descriptor error"); - if (rawp) - free(rawp); - return 1; - } - - /* now for the error processing */ - switch (sg_err_category3(&io_hdr)) { - case SG_ERR_CAT_CLEAN: - break; - case SG_ERR_CAT_RECOVERED: - printf - ("Recovered error on READ BUFFER descriptor, continuing\n"); - break; - default: /* won't bother decoding other categories */ - sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr); - if (rawp) - free(rawp); - return 1; - } - - buf_capacity = ((rbBuff[1] << 16) | (rbBuff[2] << 8) | rbBuff[3]); - printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n", - buf_capacity, (int)rbBuff[0]); - - if (0 == buf_size) - buf_size = buf_capacity; - else if (buf_size > buf_capacity) { - printf - ("Requested buffer size=%d exceeds reported capacity=%d\n", - buf_size, buf_capacity); - if (rawp) - free(rawp); - return 1; - } - if (rawp) { - free(rawp); - rawp = NULL; - } - - if (!do_dio) { - k = buf_size; - if (do_mmap && (0 != (k % psz))) - k = ((k / psz) + 1) * psz; /* round up to page size */ - res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &k); - if (res < 0) - perror(ME "SG_SET_RESERVED_SIZE error"); - } - - if (do_mmap) { - rbBuff = mmap(NULL, buf_size, PROT_READ, MAP_SHARED, sg_fd, 0); - if (MAP_FAILED == rbBuff) { - if (ENOMEM == errno) - printf(ME "mmap() out of memory, try a smaller " - "buffer size than %d KB\n", - buf_size / 1024); - else - perror(ME "error using mmap()"); - return 1; - } - } else { /* non mmap-ed IO */ - rawp = malloc(buf_size + (do_dio ? psz : 0)); - if (NULL == rawp) { - printf(ME "out of memory (data)\n"); - return 1; - } - if (do_dio) /* align to page boundary */ - rbBuff = - (unsigned char *)(((unsigned long)rawp + psz - 1) & - (~(psz - 1))); - else - rbBuff = rawp; - } - - num = (total_size_mb * 1024U * 1024U) / (unsigned int)buf_size; - if (do_time) { - start_tm.tv_sec = 0; - start_tm.tv_usec = 0; - gettimeofday(&start_tm, NULL); - } - /* main data reading loop */ - for (k = 0; k < num; ++k) { - memset(rbCmdBlk, 0, RB_CMD_LEN); - rbCmdBlk[0] = RB_OPCODE; - rbCmdBlk[1] = RB_MODE_DATA; - rbCmdBlk[6] = 0xff & (buf_size >> 16); - rbCmdBlk[7] = 0xff & (buf_size >> 8); - rbCmdBlk[8] = 0xff & buf_size; -#ifdef SG_DEBUG - memset(rbBuff, 0, buf_size); -#endif - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(rbCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_buffer); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = buf_size; - if (!do_mmap) - io_hdr.dxferp = rbBuff; - io_hdr.cmdp = rbCmdBlk; - io_hdr.sbp = sense_buffer; - io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */ - io_hdr.pack_id = k; - if (do_mmap) - io_hdr.flags |= SG_FLAG_MMAP_IO; - else if (do_dio) - io_hdr.flags |= SG_FLAG_DIRECT_IO; - else if (do_quick) - io_hdr.flags |= SG_FLAG_NO_DXFER; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - if (ENOMEM == errno) - printf(ME - "SG_IO data; out of memory, try a smaller " - "buffer size than %d KB\n", - buf_size / 1024); - else - perror(ME "SG_IO READ BUFFER data error"); - if (rawp) - free(rawp); - return 1; - } - - /* now for the error processing */ - switch (sg_err_category3(&io_hdr)) { - case SG_ERR_CAT_CLEAN: - break; - case SG_ERR_CAT_RECOVERED: - printf - ("Recovered error on READ BUFFER data, continuing\n"); - break; - default: /* won't bother decoding other categories */ - sg_chk_n_print3("READ BUFFER data error", &io_hdr); - if (rawp) - free(rawp); - return 1; - } - if (do_dio && - ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != - SG_INFO_DIRECT_IO)) - dio_incomplete = 1; /* flag that dio not done (completely) */ - -#ifdef SG_DEBUG - if (clear) { - for (j = 0; j < buf_size; ++j) { - if (rbBuff[j] != 0) { - clear = 0; - break; - } - } - } -#endif - } - if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { - struct timeval res_tm; - double a, b; - - gettimeofday(&end_tm, NULL); - res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; - res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; - if (res_tm.tv_usec < 0) { - --res_tm.tv_sec; - res_tm.tv_usec += 1000000; - } - a = res_tm.tv_sec; - a += (0.000001 * res_tm.tv_usec); - b = (double)buf_size *num; - printf("time to read data from buffer was %d.%06d secs", - (int)res_tm.tv_sec, (int)res_tm.tv_usec); - if ((a > 0.00001) && (b > 511)) - printf(", %.2f MB/sec\n", b / (a * 1000000.0)); - else - printf("\n"); - } - if (dio_incomplete) - printf(">> direct IO requested but not done\n"); - printf - ("Read %u MBytes (actual %u MB, %u bytes), buffer size=%d KBytes\n", - total_size_mb, (num * buf_size) / 1048576, num * buf_size, - buf_size / 1024); - - if (rawp) - free(rawp); - res = close(sg_fd); - if (res < 0) { - perror(ME "close error"); - return 0; - } -#ifdef SG_DEBUG - if (clear) - printf("read buffer always zero\n"); - else - printf("read buffer non-zero\n"); -#endif - return 0; -} - -/* Performs a 10 byte READ CAPACITY command and fetches response. There is - * evidently a 16 byte READ CAPACITY command coming. - * Return of 0 -> success, -1 -> failure */ -int do_readcap_10(int sg_fd, int pmi, unsigned int lba, - unsigned int *last_sect, unsigned int *sect_sz) -{ - int res; - unsigned char rcCmdBlk[10] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - unsigned char rcBuff[RCAP_REPLY_LEN]; - unsigned char sense_b[SENSE_BUFF_SZ]; - sg_io_hdr_t io_hdr; - - if (pmi) { /* lbs only valid when pmi set */ - rcCmdBlk[8] |= 1; - rcCmdBlk[2] = (lba >> 24) & 0xff; - rcCmdBlk[3] = (lba >> 16) & 0xff; - rcCmdBlk[4] = (lba >> 8) & 0xff; - rcCmdBlk[5] = lba & 0xff; - } - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(rcCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = sizeof(rcBuff); - io_hdr.dxferp = rcBuff; - io_hdr.cmdp = rcCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = 60000; - - while (1) { - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("read_capacity (SG_IO) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - if (SG_ERR_CAT_MEDIA_CHANGED == res) - continue; - else if (SG_ERR_CAT_CLEAN != res) { - sg_chk_n_print3("READ CAPACITY command error", &io_hdr); - return -1; - } else - break; - } - *last_sect = ((rcBuff[0] << 24) | (rcBuff[1] << 16) | - (rcBuff[2] << 8) | rcBuff[3]); - *sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) | - (rcBuff[6] << 8) | rcBuff[7]; - return 0; -} - -int show_scsi_read_capacity(char *device) -{ - int sg_fd, k, res; - unsigned int lba = 0; - int pmi = 1; - unsigned int last_blk_addr, block_size; - char ebuff[EBUFF_SZ]; - const char *file_name = 0; - - print_msg(TEST_BREAK, __FUNCTION__); - - file_name = device; - - if ((0 == pmi) && (lba > 0)) { - fprintf(stderr, - ME "lba can only be non-zero when pmi is set\n"); - usage(); - return 1; - } - if ((sg_fd = open(file_name, O_RDONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", - file_name); - perror(ebuff); - return 1; - } - /* Just to be safe, check we have a new sg device by trying an ioctl */ - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { - printf(ME "%s doesn't seem to be a version 3 sg device\n", - file_name); - close(sg_fd); - return 1; - } - res = do_readcap_10(sg_fd, pmi, lba, &last_blk_addr, &block_size); - - if (0 == res) { - printf("Read Capacity results:\n"); - if (pmi) - printf(" PMI mode: given lba=0x%x, last block before " - "delay=0x%x\n", lba, last_blk_addr); - else - printf - (" Last block address=%u (0x%x), Number of blocks=%u\n", - last_blk_addr, last_blk_addr, last_blk_addr + 1); - printf(" Block size = %u bytes\n", block_size); - } - close(sg_fd); - return 0; -} - -int do_scsi_reset_devices(char *device, int reset_opt) -{ - int sg_fd, res, k; - int do_device_reset = 0; - int do_bus_reset = 0; - int do_host_reset = 0; - char *file_name = 0; - - switch (reset_opt) { - case DEVICE_RESET: - print_msg(TEST_BREAK, __FUNCTION__); - do_device_reset = 1; - break; - case HOST_RESET: - do_host_reset = 1; - break; - case BUS_RESET: - do_bus_reset = 1; - break; - } - - file_name = device; - - sg_fd = open(file_name, O_RDWR | O_NONBLOCK); - if (sg_fd < 0) { - perror("sg_reset: open error"); - return 1; - } - - k = SG_SCSI_RESET_NOTHING; - if (do_device_reset) { - printf("sg_reset: starting device reset\n"); - k = SG_SCSI_RESET_DEVICE; - } else if (do_bus_reset) { - printf("sg_reset: starting bus reset\n"); - k = SG_SCSI_RESET_BUS; - } else if (do_host_reset) { - printf("sg_reset: starting host reset\n"); - k = SG_SCSI_RESET_HOST; - } - - res = ioctl(sg_fd, SG_SCSI_RESET, &k); - if (res < 0) { - if (EBUSY == errno) - printf("sg_reset: BUSY, may be resetting now\n"); - else if (EIO == errno) - printf - ("sg_reset: requested type of reset may not be available\n"); - else if (EACCES == errno) - printf("sg_reset: reset requires CAP_SYS_ADMIN (root) " - "permission\n"); - else if (EINVAL == errno) - printf("sg_reset: SG_SCSI_RESET not supported\n"); - else if (EIO == errno) - printf("sg_reset: scsi_reset_provider() call failed\n"); - else - perror("sg_reset: SG_SCSI_RESET failed"); - return 1; - } - if (SG_SCSI_RESET_NOTHING == k) - printf("sg_reset: did nothing, device is normal mode\n"); - else if (SG_SCSI_RESET_DEVICE == k) - printf("sg_reset: completed device reset\n"); - else if (SG_SCSI_RESET_BUS == k) - printf("sg_reset: completed bus reset\n"); - else if (SG_SCSI_RESET_HOST == k) - printf("sg_reset: completed host reset\n"); - - if (close(sg_fd) < 0) { - perror("sg_reset: close error"); - return 1; - } - return 0; -} - -static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit, - int devofl_bit, int unitofl_bit, void *outgoing_pg, - int outgoing_len, int noisy) -{ - int res; - unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] = - { SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) | - (sf_bit << 2) | (devofl_bit << 1) | - unitofl_bit); - senddiagCmdBlk[3] = (unsigned char)((outgoing_len >> 8) & 0xff); - senddiagCmdBlk[4] = (unsigned char)(outgoing_len & 0xff); - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = SEND_DIAGNOSTIC_CMDLEN; - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = outgoing_len ? SG_DXFER_TO_DEV : SG_DXFER_NONE; - io_hdr.dxfer_len = outgoing_len; - io_hdr.dxferp = outgoing_pg; - io_hdr.cmdp = senddiagCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = LONG_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (send diagnostic) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - if (noisy) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, - "Send diagnostic error, sf_code=0x%x, " - "pf_bit=%d, sf_bit=%d ", sf_code, pf_bit, - sf_bit); - sg_chk_n_print3(ebuff, &io_hdr); - } - return -1; - } -} - -static int do_rcvdiag(int sg_fd, int pcv, int pg_code, void *resp, - int mx_resp_len, int noisy) -{ - int res; - unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTIC_CMDLEN] = - { RECEIVE_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - - rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0); - rcvdiagCmdBlk[2] = (unsigned char)(pg_code); - rcvdiagCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); - rcvdiagCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = RECEIVE_DIAGNOSTIC_CMDLEN; - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = rcvdiagCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (receive diagnostic) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - if (noisy) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, - "Receive diagnostic error, pcv=%d, " - "page_code=%x ", pcv, pg_code); - sg_chk_n_print3(ebuff, &io_hdr); - } - return -1; - } -} - -/* Get last extended self-test time from mode page 0xa (for '-e' option) */ -static int do_modes_0a(int sg_fd, void *resp, int mx_resp_len, int noisy, - int mode6) -{ - int res; - unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = - { MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - unsigned char sense_b[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - int dbd = 1; - int pc = 0; - int pg_code = 0xa; - - modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0); - modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); - if (mx_resp_len > (mode6 ? 0xff : 0xffff)) { - printf(ME "mx_resp_len too big\n"); - return -1; - } - if (mode6) { - modesCmdBlk[0] = MODE_SENSE6_CMD; - modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); - } else { - modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); - modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); - } - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN; - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = mx_resp_len; - io_hdr.dxferp = resp; - io_hdr.cmdp = modesCmdBlk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_TIMEOUT; - - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("SG_IO (mode sense) error"); - return -1; - } - res = sg_err_category3(&io_hdr); - switch (res) { - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - return 0; - default: - if (noisy) { - char ebuff[EBUFF_SZ]; - snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d, " - "pc=%d, page_code=%x ", dbd, pc, pg_code); - sg_chk_n_print3(ebuff, &io_hdr); - } - return -1; - } -} - -int do_scsi_send_diagnostics(char *device) -{ - int sg_fd, k, num, rsp_len; - char *file_name = 0; - unsigned char rsp_buff[MODE_ALLOC_LEN]; - int rsp_buff_size = MODE_ALLOC_LEN; - int self_test_code = 6; - int do_pf = 0; - int do_doff = 0; - int do_def_test = 0; - int do_uoff = 0; - int oflags = O_RDWR; - - print_msg(TEST_BREAK, __FUNCTION__); - - file_name = device; - - if ((sg_fd = open(file_name, oflags)) < 0) { - snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", - file_name); - perror(ebuff); - return 1; - } - /* Just to be safe, check we have a new sg device by trying an ioctl */ - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { - printf(ME "%s doesn't seem to be a version 3 sg device\n", - file_name); - close(sg_fd); - return 1; - } - - if (0 == do_modes_0a(sg_fd, rsp_buff, 32, 1, 0)) { - /* Assume mode sense(10) response without block descriptors */ - num = (rsp_buff[0] << 8) + rsp_buff[1] - 6; - if (num >= 0xc) { - int secs; - - secs = (rsp_buff[18] << 8) + rsp_buff[19]; - printf - ("Previous extended self-test duration=%d seconds " - "(%.2f minutes)\n", secs, secs / 60.0); - } else - printf("Extended self-test duration not available\n"); - } else - printf("Extended self-test duration (mode page 0xa) failed\n"); - - memset(rsp_buff, 0, sizeof(rsp_buff)); - if (0 == do_senddiag(sg_fd, 0, do_pf, 0, 0, 0, rsp_buff, 4, 1)) { - if (0 == do_rcvdiag(sg_fd, 0, 0, rsp_buff, rsp_buff_size, 1)) { - printf("Supported diagnostic pages response:\n"); - rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4; - for (k = 0; k < (rsp_len - 4); ++k) - printf(" %s\n", - find_page_code_desc(rsp_buff[k + 4], 0)); - } - } - - if (0 == do_senddiag(sg_fd, self_test_code, do_pf, do_def_test, - do_doff, do_uoff, NULL, 0, 1)) { - if ((5 == self_test_code) || (6 == self_test_code)) - printf("Foreground self test returned GOOD status\n"); - else if (do_def_test && (!do_doff) && (!do_uoff)) - printf("Default self test returned GOOD status\n"); - } - close(sg_fd); - return 0; -} - -static void do_start_stop(int fd, int start, int immed, int loej, - int power_conditions) -{ - unsigned char cmdblk[6] = { - START_STOP, /* Command */ - 0, /* Resvd/Immed */ - 0, /* Reserved */ - 0, /* Reserved */ - 0, /* PowCond/Resvd/LoEj/Start */ - 0 - }; /* Reserved/Flag/Link */ - unsigned char sense_b[32]; - sg_io_hdr_t io_hdr; - int k, res, debug = 1; - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - cmdblk[1] = immed & 1; - cmdblk[4] = ((power_conditions & 0xf) << 4) | - ((loej & 1) << 1) | (start & 1); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(cmdblk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_NONE; - io_hdr.dxfer_len = 0; - io_hdr.dxferp = NULL; - io_hdr.cmdp = cmdblk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_START_TIMEOUT; - - if (debug) { - printf(" Start/Stop command:"); - for (k = 0; k < 6; ++k) - printf(" %02x", cmdblk[k]); - printf("\n"); - } - - if (ioctl(fd, SG_IO, &io_hdr) < 0) { - perror("start_stop (SG_IO) error"); - return; - } - res = sg_err_category3(&io_hdr); - if (SG_ERR_CAT_MEDIA_CHANGED == res) { - fprintf(stderr, "media change report, try start_stop again\n"); - if (ioctl(fd, SG_IO, &io_hdr) < 0) { - perror("start_stop (SG_IO) error"); - return; - } - } - if (SG_ERR_CAT_CLEAN != res) { - sg_chk_n_print3("start_stop", &io_hdr); - return; - } - if (debug) - fprintf(stderr, "start_stop [%s] successful\n", - start ? "start" : "stop"); -} - -static void do_sync_cache(int fd) -{ - unsigned char cmdblk[10] = { - SYNCHRONIZE_CACHE, /* Command */ - 0, /* Immed (2) */ - 0, 0, 0, 0, /* LBA */ - 0, /* Reserved */ - 0, 0, /* No of blocks */ - 0 - }; /* Reserved/Flag/Link */ - unsigned char sense_b[32]; - sg_io_hdr_t io_hdr; - int res, debug = 1; - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(cmdblk); - io_hdr.mx_sb_len = sizeof(sense_b); - io_hdr.dxfer_direction = SG_DXFER_NONE; - io_hdr.dxfer_len = 0; - io_hdr.dxferp = NULL; - io_hdr.cmdp = cmdblk; - io_hdr.sbp = sense_b; - io_hdr.timeout = DEF_START_TIMEOUT; - - if (ioctl(fd, SG_IO, &io_hdr) < 0) { - perror("sync_cache (SG_IO) error"); - return; - } - res = sg_err_category3(&io_hdr); - if (SG_ERR_CAT_MEDIA_CHANGED == res) { - fprintf(stderr, "media change report, try sync_cache again\n"); - if (ioctl(fd, SG_IO, &io_hdr) < 0) { - perror("sync_cache (SG_IO) error"); - return; - } - } - if (SG_ERR_CAT_CLEAN != res) { - sg_chk_n_print3("sync_cache", &io_hdr); - return; - } - if (debug) - fprintf(stderr, "synchronize cache successful\n"); -} - -int do_scsi_start_stop(char *device, int startstop) -{ - int synccache = 1; - char *file_name = 0; - int fd; - int immed = 1; - int loej = 0; - int power_conds = 0; - - print_msg(TEST_BREAK, __FUNCTION__); - - file_name = device; - - fd = open(file_name, O_RDWR | O_NONBLOCK); - if (fd < 0) { - fprintf(stderr, "Error trying to open %s\n", file_name); - perror(""); - usage(); - return 2; - } - if (ioctl(fd, SG_GET_TIMEOUT, 0) < 0) { - fprintf(stderr, "Given file not block or SCSI " - "generic device\n"); - close(fd); - return 3; - } - - if (synccache) - do_sync_cache(fd); - - if (power_conds > 0) - do_start_stop(fd, 0, immed, 0, power_conds); - else if (startstop != -1) - do_start_stop(fd, startstop, immed, loej, 0); - - close(fd); - return 0; -} - -int find_out_about_buffer(int sg_fd, int *buf_capacity, char *file_name) -{ - int res, buf_granul = 255; - unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + 512); - struct sg_header *rsghp = (struct sg_header *)rbBuff; - int rbInLen = OFF + RB_DESC_LEN; - int rbOutLen = OFF + sizeof(rbCmdBlk); - unsigned char *buffp = rbBuff + OFF; - rsghp->pack_len = 0; /* don't care */ - rsghp->pack_id = 0; - rsghp->reply_len = rbInLen; - rsghp->twelve_byte = 0; - rsghp->result = 0; -#ifndef SG_GET_RESERVED_SIZE - rsghp->sense_buffer[0] = 0; -#endif - memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk)); - rbBuff[OFF + 1] = RB_MODE_DESC; - rbBuff[OFF + 8] = RB_DESC_LEN; - - res = write(sg_fd, rbBuff, rbOutLen); - if (res < 0) { - perror("sg_test_rwbuf: write (desc) error"); - if (rbBuff) - free(rbBuff); - return 1; - } - if (res < rbOutLen) { - printf("sg_test_rwbuf: wrote less (desc), ask=%d, got=%d\n", - rbOutLen, res); - if (rbBuff) - free(rbBuff); - return 1; - } - - memset(rbBuff + OFF, 0, RB_DESC_LEN); - res = read(sg_fd, rbBuff, rbInLen); - if (res < 0) { - perror("sg_test_rwbuf: read (desc) error"); - if (rbBuff) - free(rbBuff); - return 1; - } - if (res < rbInLen) { - printf("sg_test_rwbuf: read less (desc), ask=%d, got=%d\n", - rbInLen, res); - if (rbBuff) - free(rbBuff); - return 1; - } -#ifdef SG_GET_RESERVED_SIZE - if (!sg_chk_n_print("sg_test_rwbuf: desc", rsghp->target_status, - rsghp->host_status, rsghp->driver_status, - rsghp->sense_buffer, SG_MAX_SENSE)) { - printf - ("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n", - file_name); - if (rbBuff) - free(rbBuff); - return 1; - } -#else - if ((rsghp->result != 0) || (0 != rsghp->sense_buffer[0])) { - printf("sg_test_rwbuf: read(desc) result=%d\n", rsghp->result); - if (0 != rsghp->sense_buffer[0]) - sg_print_sense("sg_test_rwbuf: desc", - rsghp->sense_buffer, SG_MAX_SENSE); - printf - ("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n", - file_name); - if (rbBuff) - free(rbBuff); - return 1; - } -#endif - *(buf_capacity) = ((buffp[1] << 16) | (buffp[2] << 8) | buffp[3]); - buf_granul = (unsigned char)buffp[0]; - - printf("READ BUFFER reports: %02x %02x %02x %02x %02x %02x %02x %02x\n", - buffp[0], buffp[1], buffp[2], buffp[3], - buffp[4], buffp[5], buffp[6], buffp[7]); - - printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n", - *(buf_capacity), buf_granul); -#ifdef SG_DEF_RESERVED_SIZE - res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, buf_capacity); - if (res < 0) - perror("sg_test_rwbuf: SG_SET_RESERVED_SIZE error"); -#endif - return 0; -} - -int mymemcmp(unsigned char *bf1, unsigned char *bf2, int len) -{ - int df; - for (df = 0; df < len; df++) - if (bf1[df] != bf2[df]) - return df; - return 0; -} - -int do_checksum(int *buf, int len, int quiet) -{ - int sum = base; - int i; - int rln = len; - for (i = 0; i < len / BPI; i++) - sum += buf[i]; - while (rln % BPI) - sum += ((char *)buf)[--rln]; - if (sum != READWRITE_BASE_NUM) { - if (!quiet) - printf("sg_test_rwbuf: Checksum error (sz=%i): %08x\n", - len, sum); - if (cmpbuf && !quiet) { - int diff = mymemcmp(cmpbuf, (unsigned char *)buf, len); - printf("Differ at pos %i/%i:\n", diff, len); - for (i = 0; i < 24 && i + diff < len; i++) - printf(" %02x", cmpbuf[i + diff]); - printf("\n"); - for (i = 0; i < 24 && i + diff < len; i++) - printf(" %02x", - ((unsigned char *)buf)[i + diff]); - printf("\n"); - } - return 2; - } else - return 0; -} - -void do_fill_buffer(int *buf, int len) -{ - int sum; - int i; - int rln = len; - srand(time(0)); -retry: - if (len >= BPI) - base = READWRITE_BASE_NUM + rand(); - else - base = READWRITE_BASE_NUM + (char)rand(); - sum = base; - for (i = 0; i < len / BPI - 1; i++) { - /* we rely on rand() giving full range of int */ - buf[i] = rand(); - sum += buf[i]; - } - while (rln % BPI) { - ((char *)buf)[--rln] = rand(); - sum += ((char *)buf)[rln]; - } - if (len >= BPI) - buf[len / BPI - 1] = READWRITE_BASE_NUM - sum; - else - ((char *)buf)[0] = READWRITE_BASE_NUM + ((char *)buf)[0] - sum; - if (do_checksum(buf, len, 1)) { - if (len < BPI) - goto retry; - printf("sg_test_rwbuf: Memory corruption?\n"); - exit(1); - } - if (cmpbuf) - memcpy(cmpbuf, (char *)buf, len); -} - -int read_buffer(int sg_fd, unsigned size) -{ - int res; - unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size); - struct sg_header *rsghp = (struct sg_header *)rbBuff; - - int rbInLen = OFF + size; - int rbOutLen = OFF + sizeof(rbCmdBlk); - memset(rbBuff, 0, OFF + sizeof(rbCmdBlk) + size); - rsghp->pack_len = 0; /* don't care */ - rsghp->reply_len = rbInLen; - rsghp->twelve_byte = 0; - rsghp->result = 0; - memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk)); - rbBuff[OFF + 1] = RB_MODE_DATA; - rbBuff[OFF + 6] = 0xff & ((size) >> 16); - rbBuff[OFF + 7] = 0xff & ((size) >> 8); - rbBuff[OFF + 8] = 0xff & (size); - - rsghp->pack_id = 2; - res = write(sg_fd, rbBuff, rbOutLen); - if (res < 0) { - perror("sg_test_rwbuf: write (data) error"); - if (rbBuff) - free(rbBuff); - return 1; - } - if (res < rbOutLen) { - printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n", - rbOutLen, res); - if (rbBuff) - free(rbBuff); - return 1; - } - - res = read(sg_fd, rbBuff, rbInLen); - if (res < 0) { - perror("sg_test_rwbuf: read (data) error"); - if (rbBuff) - free(rbBuff); - return 1; - } - if (res < rbInLen) { - printf("sg_test_rwbuf: read less (data), ask=%d, got=%d\n", - rbInLen, res); - if (rbBuff) - free(rbBuff); - return 1; - } - res = do_checksum((int *)(rbBuff + OFF), size, 0); - if (rbBuff) - free(rbBuff); - return res; -} - -int write_buffer(int sg_fd, unsigned size) -{ - int res; - unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size); - struct sg_header *rsghp = (struct sg_header *)rbBuff; - //unsigned char * buffp = rbBuff + OFF; - - int rbInLen = OFF; - int rbOutLen = OFF + sizeof(rbCmdBlk) + size; - - do_fill_buffer((int *)(rbBuff + OFF + sizeof(rbCmdBlk)), size); - rsghp->pack_len = 0; /* don't care */ - rsghp->reply_len = rbInLen; - rsghp->twelve_byte = 0; - rsghp->result = 0; - memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk)); - rbBuff[OFF + 0] = WRITE_BUFFER; - rbBuff[OFF + 1] = RB_MODE_DATA; - rbBuff[OFF + 6] = 0xff & ((size) >> 16); - rbBuff[OFF + 7] = 0xff & ((size) >> 8); - rbBuff[OFF + 8] = 0xff & (size); - - rsghp->pack_id = 1; - res = write(sg_fd, rbBuff, rbOutLen); - if (res < 0) { - perror("sg_test_rwbuf: write (data) error"); - if (rbBuff) - free(rbBuff); - return 1; - } - if (res < rbOutLen) { - printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n", - rbOutLen, res); - if (rbBuff) - free(rbBuff); - return 1; - } - - res = read(sg_fd, rbBuff, rbInLen); - if (res < 0) { - perror("sg_test_rwbuf: read (status) error"); - if (rbBuff) - free(rbBuff); - return 1; - } - if (rbBuff) - free(rbBuff); - return 0; -} - -int do_scsi_read_write_buffer(char *device) -{ - int sg_fd; - int res, buf_capacity; - char *file_name = device; - struct stat a_st; - int block_dev = 0; - - print_msg(TEST_BREAK, __FUNCTION__); - - sg_fd = open(file_name, O_RDWR); - if (sg_fd < 0) { - perror("sg_test_rwbuf: open error"); - return 1; - } - if (fstat(sg_fd, &a_st) < 0) { - fprintf(stderr, "could do fstat() on fd ??\n"); - close(sg_fd); - return 1; - } - if (S_ISBLK(a_st.st_mode)) - block_dev = 1; - /* Don't worry, being very careful not to write to a none-sg file ... */ - if (block_dev || (ioctl(sg_fd, SG_GET_TIMEOUT, 0) < 0)) { - /* perror("ioctl on generic device, error"); */ - printf("sg_test_rwbuf: not a sg device, or wrong driver\n"); - return 1; - } - if (find_out_about_buffer(sg_fd, &buf_capacity, file_name)) - return 1; - - cmpbuf = malloc(buf_capacity); - if (write_buffer(sg_fd, buf_capacity)) - return 3; - res = read_buffer(sg_fd, buf_capacity); - if (res) - return (res + 4); - - res = close(sg_fd); - if (res < 0) { - perror("sg_test_rwbuf: close error"); - return 6; - } - printf("Success\n"); - return 0; -} - -int do_scsi_test_unit_ready(char *device) -{ - int sg_fd, k; - unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; - sg_io_hdr_t io_hdr; - char *file_name = device; - char ebuff[EBUFF_SZ]; - unsigned char sense_buffer[32]; - int num_turs = 10240; - int num_errs = 0; - int do_time = 1; - struct timeval start_tm, end_tm; - - print_msg(TEST_BREAK, __FUNCTION__); - - if ((sg_fd = open(file_name, O_RDONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, - "sg_turs: error opening file: %s", file_name); - perror(ebuff); - return 1; - } - /* Just to be safe, check we have a new sg driver by trying an ioctl */ - if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { - printf - ("sg_turs: %s isn't an sg device (or the sg driver is old)\n", - file_name); - close(sg_fd); - return 1; - } - /* Prepare TEST UNIT READY command */ - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(turCmdBlk); - io_hdr.mx_sb_len = sizeof(sense_buffer); - io_hdr.dxfer_direction = SG_DXFER_NONE; - io_hdr.cmdp = turCmdBlk; - io_hdr.sbp = sense_buffer; - io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */ - if (do_time) { - start_tm.tv_sec = 0; - start_tm.tv_usec = 0; - gettimeofday(&start_tm, NULL); - } - for (k = 0; k < num_turs; ++k) { - io_hdr.pack_id = k; - if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { - perror("sg_turs: Test Unit Ready SG_IO ioctl error"); - close(sg_fd); - return 1; - } - if (io_hdr.info & SG_INFO_OK_MASK) { - ++num_errs; - if (1 == num_turs) { /* then print out the error message */ - if (SG_ERR_CAT_CLEAN != - sg_err_category3(&io_hdr)) - sg_chk_n_print3("tur", &io_hdr); - } - } - } - if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { - struct timeval res_tm; - double a, b; - - gettimeofday(&end_tm, NULL); - res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; - res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; - if (res_tm.tv_usec < 0) { - --res_tm.tv_sec; - res_tm.tv_usec += 1000000; - } - a = res_tm.tv_sec; - a += (0.000001 * res_tm.tv_usec); - b = (double)num_turs; - printf("time to perform commands was %d.%06d secs", - (int)res_tm.tv_sec, (int)res_tm.tv_usec); - if (a > 0.00001) - printf("; %.2f operations/sec\n", b / a); - else - printf("\n"); - } - - printf("Completed %d Test Unit Ready commands with %d errors\n", - num_turs, num_errs); - close(sg_fd); - return 0; -} - -/* Returns 0 -> ok, 1 -> err, 2 -> recovered error */ -static int do_sg_io(int sg_fd, unsigned char *buff) -{ -/* N.B. Assuming buff contains pointer 'buffer' or 'buffer1' */ - struct sg_header *sghp = (struct sg_header *)(buff - OFF); - int res; - - sghp->pack_len = 0; - sghp->reply_len = SG_HSZ + *(((int *)buff) + 1); - sghp->pack_id = 0; - sghp->twelve_byte = 0; - sghp->other_flags = 0; -#ifndef SG_GET_RESERVED_SIZE - sghp->sense_buffer[0] = 0; -#endif -#if 0 - sg_print_command(buff + 8); - printf(" write_len=%d, read_len=%d\n", - SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff), - sghp->reply_len); -#endif - res = write(sg_fd, (const void *)sghp, - SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff)); - if (res < 0) { -#ifdef SG_IO_DEBUG - perror("write to sg failed"); -#endif - return 1; - } - res = read(sg_fd, (void *)sghp, sghp->reply_len); - if (res < 0) { -#ifdef SG_IO_DEBUG - perror("read from sg failed"); -#endif - return 1; - } -#ifdef SG_GET_RESERVED_SIZE - res = sg_err_category(sghp->target_status, sghp->host_status, - sghp->driver_status, sghp->sense_buffer, - SG_MAX_SENSE); - switch (res) { - case SG_ERR_CAT_CLEAN: - return 0; - case SG_ERR_CAT_RECOVERED: - return 2; - default: -#ifdef SG_IO_DEBUG - sg_chk_n_print("read from sg", sghp->target_status, - sghp->host_status, sghp->driver_status, - sghp->sense_buffer, SG_MAX_SENSE); -#endif - return 1; - } -#else - if (0 != sghp->sense_buffer[0]) { -#ifdef SG_IO_DEBUG - int k; - printf("read from sg, sense buffer (in hex):\n "); - for (k = 0; k < 16; ++k) - printf("%02x ", (int)sghp->sense_buffer[k]); - printf("\n"); -#endif - return 1; - } else if (0 != sghp->result) { -#ifdef SG_IO_DEBUG - printf("read from sg, bad result=%d\n", sghp->result); -#endif - return 1; - } else - return 0; -#endif -} - -static char *get_page_name(int pageno) -{ - if ((pageno <= 0) || (pageno >= MAX_PAGENO) || (!page_names[pageno])) - return "Mode"; - return page_names[pageno]; -} - -static int getnbyte(unsigned char *pnt, int nbyte) -{ - unsigned int result; - int i; - result = 0; - for (i = 0; i < nbyte; i++) - result = (result << 8) | (pnt[i] & 0xff); - return result; -} - -static void bitfield(unsigned char *pageaddr, char *text, int mask, int shift) -{ - printf("%-35s%d\n", text, (*pageaddr >> shift) & mask); -} - -static void notbitfield(unsigned char *pageaddr, char *text, int mask, - int shift) -{ - printf("%-35s%d\n", text, !((*pageaddr >> shift) & mask)); -} - -static void intfield(unsigned char *pageaddr, int nbytes, char *text) -{ - printf("%-35s%d\n", text, getnbyte(pageaddr, nbytes)); -} - -static void hexfield(unsigned char *pageaddr, int nbytes, char *text) -{ - printf("%-35s0x%x\n", text, getnbyte(pageaddr, nbytes)); -} - -static void hexdatafield(unsigned char *pageaddr, int nbytes, char *text) -{ - printf("%-35s0x", text); - while (nbytes-- > 0) - printf("%02x", *pageaddr++); - putchar('\n'); -} - -static int get_mode_page(int page, int page_code) -{ - int status, quiet; - unsigned char *cmd; - - memset(buffer, 0, SIZEOF_BUFFER); - - quiet = page_code & ~3; - page_code &= 3; - - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = 0xff; /* length of output data */ - - cmd = (unsigned char *)(((int *)buffer) + 2); - - cmd[0] = MODE_SENSE; /* MODE SENSE (6) */ - cmd[1] = 0x00; /* lun = 0, inhibitting BD makes this fail - for me */ - cmd[2] = (page_code << 6) | page; - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = (unsigned char)0xff; /* allocation length */ - cmd[5] = 0x00; /* control */ - - status = do_sg_io(glob_fd, buffer); - if (status && (!quiet)) - fprintf(stdout, ">>> Unable to read %s Page %02xh\n", - get_page_name(page), page); - //dump (buffer+2, 46); - return status; -} - -/* Same as above, but this time with MODE_SENSE_10 */ -static int get_mode_page10(int page, int page_code) -{ - int status, quiet; - unsigned char *cmd; - - memset(buffer, 0, SIZEOF_BUFFER); - - quiet = page_code & ~3; - page_code &= 3; - - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = 0xffff; /* length of output buffer */ - - cmd = (unsigned char *)(((int *)buffer) + 2); - - cmd[0] = MODE_SENSE_10; /* MODE SENSE (10) */ - cmd[1] = 0x00; /* lun = 0, inhibitting BD makes this fail - for me */ - cmd[2] = (page_code << 6) | page; - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = 0x00; /* (reserved) */ - cmd[5] = 0x00; /* (reserved) */ - cmd[6] = 0x00; /* (reserved) */ - cmd[7] = 0xff; /* allocation length hi */ - cmd[8] = 0xff; /* allocation length lo */ - cmd[9] = 0x00; /* control */ - - status = do_sg_io(glob_fd, buffer); - if (status && (!quiet)) - fprintf(stdout, - ">>> Unable to read %s Page %02xh with MODESENSE(10)\n", - get_page_name(page), page); - return status; -} - -/* Contents should point to the mode parameter header that we obtained - in a prior read operation. This way we do not have to work out the - format of the beast */ - -static int read_geometry(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(4, 9); - - printf("Data from Rigid Disk Drive Geometry Page\n"); - printf("----------------------------------------\n"); - intfield(pagestart + 2, 3, "Number of cylinders"); - intfield(pagestart + 5, 1, "Number of heads"); - intfield(pagestart + 6, 3, "Starting write precomp"); - intfield(pagestart + 9, 3, "Starting reduced current"); - intfield(pagestart + 12, 2, "Drive step rate"); - intfield(pagestart + 14, 3, "Landing Zone Cylinder"); - bitfield(pagestart + 17, "RPL", 3, 0); - intfield(pagestart + 18, 1, "Rotational Offset"); - intfield(pagestart + 20, 2, "Rotational Rate"); - printf("\n"); - return 0; - -} - -static int read_disconnect_reconnect_data(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(2, 7); - - printf("Data from Disconnect-Reconnect Page\n"); - printf("-----------------------------------\n"); - intfield(pagestart + 2, 1, "Buffer full ratio"); - intfield(pagestart + 3, 1, "Buffer empty ratio"); - intfield(pagestart + 4, 2, "Bus Inactivity Limit"); - intfield(pagestart + 6, 2, "Disconnect Time Limit"); - intfield(pagestart + 8, 2, "Connect Time Limit"); - intfield(pagestart + 10, 2, "Maximum Burst Size"); - hexfield(pagestart + 12, 1, "DTDC"); - printf("\n"); - return 0; - -} - -static int read_control_page(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(10, 9); - - printf("Data from Control Page\n"); - printf("----------------------\n"); - bitfield(pagestart + 2, "RLEC", 1, 0); - bitfield(pagestart + 3, "QErr", 1, 1); - bitfield(pagestart + 3, "DQue", 1, 0); - bitfield(pagestart + 4, "EECA", 1, 7); - bitfield(pagestart + 4, "RAENP", 1, 2); - bitfield(pagestart + 4, "UUAENP", 1, 1); - bitfield(pagestart + 4, "EAENP", 1, 0); - bitfield(pagestart + 3, "Queue Algorithm Modifier", 0xf, 4); - intfield(pagestart + 6, 2, "Ready AEN Holdoff Period"); - printf("\n"); - return 0; - -} - -static int error_recovery_page(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(1, 14); - printf("Data from Error Recovery Page\n"); - printf("-----------------------------\n"); - bitfield(pagestart + 2, "AWRE", 1, 7); - bitfield(pagestart + 2, "ARRE", 1, 6); - bitfield(pagestart + 2, "TB", 1, 5); - bitfield(pagestart + 2, "RC", 1, 4); - bitfield(pagestart + 2, "EER", 1, 3); - bitfield(pagestart + 2, "PER", 1, 2); - bitfield(pagestart + 2, "DTE", 1, 1); - bitfield(pagestart + 2, "DCR", 1, 0); - intfield(pagestart + 3, 1, "Read Retry Count"); - intfield(pagestart + 4, 1, "Correction Span"); - intfield(pagestart + 5, 1, "Head Offset Count"); - intfield(pagestart + 6, 1, "Data Strobe Offset Count"); - intfield(pagestart + 8, 1, "Write Retry Count"); - intfield(pagestart + 10, 2, "Recovery Time Limit"); - printf("\n"); - return 0; -} - -static int notch_parameters_page(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(0xc, 7); - - printf("Data from Notch Parameters Page\n"); - printf("-------------------------------\n"); - bitfield(pagestart + 2, "Notched Drive", 1, 7); - bitfield(pagestart + 2, "Logical or Physical Notch", 1, 6); - intfield(pagestart + 4, 2, "Max # of notches"); - intfield(pagestart + 6, 2, "Active Notch"); - if (pagestart[2] & 0x40) { - intfield(pagestart + 8, 4, "Starting Boundary"); - intfield(pagestart + 12, 4, "Ending Boundary"); - } else { /* Hex is more meaningful for physical notches */ - hexfield(pagestart + 8, 4, "Starting Boundary"); - hexfield(pagestart + 12, 4, "Ending Boundary"); - } - - printf("0x%8.8x%8.8x", getnbyte(pagestart + 16, 4), - getnbyte(pagestart + 20, 4)); - - printf("\n"); - return 0; -} - -static char *formatname(int format) -{ - switch (format) { - case 0x0: - return "logical blocks"; - case 0x4: - return "bytes from index [Cyl:Head:Off]\n" - "Offset -1 marks whole track as bad.\n"; - case 0x5: - return "physical blocks [Cyl:Head:Sect]\n" - "Sector -1 marks whole track as bad.\n"; - } - return "Weird, unknown format"; -} - -static int read_defect_list(int page_code) -{ - int status = 0, i, len, reallen, table, k; - unsigned char *cmd, *df = 0; - int trunc; - - printf("Data from Defect Lists\n" "----------------------\n"); - for (table = 0; table < 2; table++) { - memset(buffer, 0, SIZEOF_BUFFER); - trunc = 0; - - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = 4; /* length of output buffer */ - - cmd = (unsigned char *)(((int *)buffer) + 2); - - cmd[0] = 0x37; /* READ DEFECT DATA */ - cmd[1] = 0x00; /* lun=0 */ - cmd[2] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */ - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = 0x00; /* (reserved) */ - cmd[5] = 0x00; /* (reserved) */ - cmd[6] = 0x00; /* (reserved) */ - cmd[7] = 0x00; /* Alloc len */ - cmd[8] = 0x04; /* Alloc len */ - cmd[9] = 0x00; /* control */ - - i = do_sg_io(glob_fd, buffer); - if (2 == i) - i = 0; /* Recovered error, probably returned a different - format */ - if (i) { - fprintf(stdout, ">>> Unable to read %s defect data.\n", - (table ? "grown" : "manufacturer")); - status |= i; - continue; - } - len = (buffer[10] << 8) | buffer[11]; - reallen = len; - if (len > 0) { - if (len >= 0xfff8) { - len = SIZEOF_BUFFER - 8; - k = len + 8; /* length of defect list */ - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = k; /* length of output buffer */ - ((struct sg_header *)buffer)->twelve_byte = 1; - cmd[0] = 0xB7; /* READ DEFECT DATA */ - cmd[1] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */ - cmd[2] = 0x00; /* (reserved) */ - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = 0x00; /* (reserved) */ - cmd[5] = 0x00; /* (reserved) */ - cmd[6] = 0x00; /* Alloc len */ - cmd[7] = (k >> 16); /* Alloc len */ - cmd[8] = (k >> 8); /* Alloc len */ - cmd[9] = (k & 0xff); /* Alloc len */ - cmd[10] = 0x00; /* reserved */ - cmd[11] = 0x00; /* control */ - i = do_sg_io(glob_fd, buffer); - if (i == 2) - i = 0; - if (i) - goto trytenbyte; - reallen = - (buffer[12] << 24 | buffer[13] << 16 | - buffer[14] << 8 | buffer[15]); - len = reallen; - if (len > SIZEOF_BUFFER - 8) { - len = SIZEOF_BUFFER - 8; - trunc = 1; - } - df = (unsigned char *)(buffer + 16); - } else { -trytenbyte: - if (len > 0xfff8) { - len = 0xfff8; - trunc = 1; - } - k = len + 4; /* length of defect list */ - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = k; /* length of output buffer */ - cmd[0] = 0x37; /* READ DEFECT DATA */ - cmd[1] = 0x00; /* lun=0 */ - cmd[2] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */ - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = 0x00; /* (reserved) */ - cmd[5] = 0x00; /* (reserved) */ - cmd[6] = 0x00; /* (reserved) */ - cmd[7] = (k >> 8); /* Alloc len */ - cmd[8] = (k & 0xff); /* Alloc len */ - cmd[9] = 0x00; /* control */ - i = do_sg_io(glob_fd, buffer); - df = (unsigned char *)(buffer + 12); - } - } - if (2 == i) - i = 0; /* Recovered error, probably returned a different - format */ - if (i) { - fprintf(stdout, ">>> Unable to read %s defect data.\n", - (table ? "grown" : "manufacturer")); - status |= i; - continue; - } else { - if (table && !status) - printf("\n"); - printf("%d entries (%d bytes) in %s table.\n" - "Format (%x) is: %s\n", - reallen / ((buffer[9] & 7) ? 8 : 4), reallen, - (table ? "grown" : "manufacturer"), - buffer[9] & 7, formatname(buffer[9] & 7)); - i = 0; - if ((buffer[9] & 7) == 4) { - while (len > 0) { - snprintf((char *)buffer, 40, - "%6d:%3u:%8d", getnbyte(df, 3), - df[3], getnbyte(df + 4, 4)); - printf("%19s", (char *)buffer); - len -= 8; - df += 8; - i++; - if (i >= 4) { - printf("\n"); - i = 0; - } else - printf("|"); - } - } else if ((buffer[9] & 7) == 5) { - while (len > 0) { - snprintf((char *)buffer, 40, - "%6d:%2u:%5d", getnbyte(df, 3), - df[3], getnbyte(df + 4, 4)); - printf("%15s", (char *)buffer); - len -= 8; - df += 8; - i++; - if (i >= 5) { - printf("\n"); - i = 0; - } else - printf("|"); - } - } else { - while (len > 0) { - printf("%10d", getnbyte(df, 4)); - len -= 4; - df += 4; - i++; - if (i >= 7) { - printf("\n"); - i = 0; - } else - printf("|"); - } - } - if (i) - printf("\n"); - } - if (trunc) - printf("[truncated]\n"); - } - printf("\n"); - return status; -} - -static int read_cache(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(8, 9); - - printf("Data from Caching Page\n"); - printf("----------------------\n"); - bitfield(pagestart + 2, "Write Cache", 1, 2); - notbitfield(pagestart + 2, "Read Cache", 1, 0); - bitfield(pagestart + 2, "Prefetch units", 1, 1); - bitfield(pagestart + 3, "Demand Read Retention Priority", 0xf, 4); - bitfield(pagestart + 3, "Demand Write Retention Priority", 0xf, 0); - intfield(pagestart + 4, 2, "Disable Pre-fetch Transfer Length"); - intfield(pagestart + 6, 2, "Minimum Pre-fetch"); - intfield(pagestart + 8, 2, "Maximum Pre-fetch"); - intfield(pagestart + 10, 2, "Maximum Pre-fetch Ceiling"); - printf("\n"); - return 0; -} - -static int read_format_info(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(3, 13); - - printf("Data from Format Device Page\n"); - printf("----------------------------\n"); - bitfield(pagestart + 20, "Removable Medium", 1, 5); - bitfield(pagestart + 20, "Supports Hard Sectoring", 1, 6); - bitfield(pagestart + 20, "Supports Soft Sectoring", 1, 7); - bitfield(pagestart + 20, "Addresses assigned by surface", 1, 4); - intfield(pagestart + 2, 2, "Tracks per Zone"); - intfield(pagestart + 4, 2, "Alternate sectors per zone"); - intfield(pagestart + 6, 2, "Alternate tracks per zone"); - intfield(pagestart + 8, 2, "Alternate tracks per lun"); - intfield(pagestart + 10, 2, "Sectors per track"); - intfield(pagestart + 12, 2, "Bytes per sector"); - intfield(pagestart + 14, 2, "Interleave"); - intfield(pagestart + 16, 2, "Track skew factor"); - intfield(pagestart + 18, 2, "Cylinder skew factor"); - printf("\n"); - return 0; - -} - -static int verify_error_recovery(int page_code) -{ - int status; - int bdlen; - unsigned char *pagestart; - - SETUP_MODE_PAGE(7, 7); - - printf("Data from Verify Error Recovery Page\n"); - printf("------------------------------------\n"); - bitfield(pagestart + 2, "EER", 1, 3); - bitfield(pagestart + 2, "PER", 1, 2); - bitfield(pagestart + 2, "DTE", 1, 1); - bitfield(pagestart + 2, "DCR", 1, 0); - intfield(pagestart + 3, 1, "Verify Retry Count"); - intfield(pagestart + 4, 1, "Verify Correction Span (bits)"); - intfield(pagestart + 10, 2, "Verify Recovery Time Limit (ms)"); - - printf("\n"); - return 0; -} - -static int peripheral_device_page(int page_code) -{ - static char *idents[] = { - "X3.131: Small Computer System Interface", - "X3.91M-1987: Storage Module Interface", - "X3.170: Enhanced Small Device Interface", - "X3.130-1986; X3T9.3/87-002: IPI-2", - "X3.132-1987; X3.147-1988: IPI-3" - }; - int status; - int bdlen; - unsigned ident; - unsigned char *pagestart; - char *name; - - SETUP_MODE_PAGE(9, 2); - - printf("Data from Peripheral Device Page\n"); - printf("--------------------------------\n"); - - ident = getnbyte(pagestart + 2, 2); - if (ident < (sizeof(idents) / sizeof(char *))) - name = idents[ident]; - else if (ident < 0x8000) - name = "Reserved"; - else - name = "Vendor Specific"; - - bdlen = pagestart[1] - 6; - if (bdlen < 0) - bdlen = 0; - else - SETUP_MODE_PAGE(9, 2); - - hexfield(pagestart + 2, 2, "Interface Identifier"); - for (ident = 0; ident < 35; ident++) - putchar(' '); - puts(name); - - hexdatafield(pagestart + 8, bdlen, "Vendor Specific Data"); - - printf("\n"); - return 0; -} - -/* end */ - -static int do_user_page(int page_code, int page_no) -{ - int status; - int bdlen; - int i; - //unsigned ident; - unsigned char *pagestart; - char *name; - - SETUP_MODE_PAGE(page_no, 0); - //printf ("Page 0x%02x len: %i\n", page_code, pagestart[1]); - - name = "Vendor specific"; - for (i = 2; i < pagestart[1] + 2; i++) { - char nm[8]; - snprintf(nm, 8, "%02x", i); - hexdatafield(pagestart + i, 1, nm); - } - - printf("\n"); - puts(name); - return 0; -} - -/* end */ - -static int do_scsi_info_inquiry(int page_code) -{ - int status, i, x_interface = 0; - unsigned char *cmd; - unsigned char *pagestart; - unsigned char tmp; - - for (i = 0; i < 1024; i++) { - buffer[i] = 0; - } - - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = 36; /* length of output buffer */ - - cmd = (unsigned char *)(((int *)buffer) + 2); - - cmd[0] = 0x12; /* INQUIRY */ - cmd[1] = 0x00; /* lun=0, evpd=0 */ - cmd[2] = 0x00; /* page code = 0 */ - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = 0x24; /* allocation length */ - cmd[5] = 0x00; /* control */ - - status = do_sg_io(glob_fd, buffer); - if (status) { - printf("Error doing INQUIRY (1)"); - return status; - } - - pagestart = buffer + 8; - - printf("Inquiry command\n"); - printf("---------------\n"); - bitfield(pagestart + 7, "Relative Address", 1, 7); - bitfield(pagestart + 7, "Wide bus 32", 1, 6); - bitfield(pagestart + 7, "Wide bus 16", 1, 5); - bitfield(pagestart + 7, "Synchronous neg.", 1, 4); - bitfield(pagestart + 7, "Linked Commands", 1, 3); - bitfield(pagestart + 7, "Command Queueing", 1, 1); - bitfield(pagestart + 7, "SftRe", 1, 0); - bitfield(pagestart + 0, "Device Type", 0x1f, 0); - bitfield(pagestart + 0, "Peripheral Qualifier", 0x7, 5); - bitfield(pagestart + 1, "Removable?", 1, 7); - bitfield(pagestart + 1, "Device Type Modifier", 0x7f, 0); - bitfield(pagestart + 2, "ISO Version", 3, 6); - bitfield(pagestart + 2, "ECMA Version", 7, 3); - bitfield(pagestart + 2, "ANSI Version", 7, 0); - bitfield(pagestart + 3, "AENC", 1, 7); - bitfield(pagestart + 3, "TrmIOP", 1, 6); - bitfield(pagestart + 3, "Response Data Format", 0xf, 0); - tmp = pagestart[16]; - pagestart[16] = 0; - printf("%s%s\n", (!x_interface ? "Vendor: " : ""), - pagestart + 8); - pagestart[16] = tmp; - - tmp = pagestart[32]; - pagestart[32] = 0; - printf("%s%s\n", (!x_interface ? "Product: " : ""), - pagestart + 16); - pagestart[32] = tmp; - - printf("%s%s\n", (!x_interface ? "Revision level: " : ""), - pagestart + 32); - - printf("\n"); - return status; - -} - -static int do_serial_number(int page_code) -{ - int status, i, pagelen; - unsigned char *cmd; - unsigned char *pagestart; - - for (i = 0; i < 1024; i++) { - buffer[i] = 0; - } - - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = 4; /* length of output buffer */ - - cmd = (unsigned char *)(((int *)buffer) + 2); - - cmd[0] = 0x12; /* INQUIRY */ - cmd[1] = 0x01; /* lun=0, evpd=1 */ - cmd[2] = 0x80; /* page code = 0x80, serial number */ - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = 0x04; /* allocation length */ - cmd[5] = 0x00; /* control */ - - status = do_sg_io(glob_fd, buffer); - if (status) { - printf("Error doing INQUIRY (evpd=1, serial number)\n"); - return status; - } - - pagestart = buffer + 8; - - pagelen = 4 + pagestart[3]; - *((int *)buffer) = 0; /* length of input data */ - *(((int *)buffer) + 1) = pagelen; /* length of output buffer */ - - cmd[0] = 0x12; /* INQUIRY */ - cmd[1] = 0x01; /* lun=0, evpd=1 */ - cmd[2] = 0x80; /* page code = 0x80, serial number */ - cmd[3] = 0x00; /* (reserved) */ - cmd[4] = (unsigned char)pagelen; /* allocation length */ - cmd[5] = 0x00; /* control */ - - status = do_sg_io(glob_fd, buffer); - if (status) { - printf("Error doing INQUIRY (evpd=1, serial number, len)\n"); - return status; - } - - printf("Serial Number '"); - for (i = 0; i < pagestart[3]; i++) - printf("%c", pagestart[4 + i]); - printf("'\n"); - printf("\n"); - - return status; -} - -/* Print out a list of the known devices on the system */ -static void show_devices() -{ - int k, j, fd, err, bus; - My_scsi_idlun m_idlun; - char name[MDEV_NAME_SZ]; - char ebuff[EBUFF_SZ]; - int do_numeric = 1; - int max_holes = MAX_HOLES; - - for (k = 0, j = 0; k < sizeof(devices) / sizeof(char *); k++) { - fd = open(devices[k], O_RDONLY | O_NONBLOCK); - if (fd < 0) - continue; - err = - ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &(sg_map_arr[j].bus)); - if (err < 0) { - snprintf(ebuff, EBUFF_SZ, - "SCSI(1) ioctl on %s failed", devices[k]); - perror(ebuff); - close(fd); - continue; - } - err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun); - if (err < 0) { - snprintf(ebuff, EBUFF_SZ, - "SCSI(2) ioctl on %s failed", devices[k]); - perror(ebuff); - close(fd); - continue; - } - sg_map_arr[j].channel = (m_idlun.dev_id >> 16) & 0xff; - sg_map_arr[j].lun = (m_idlun.dev_id >> 8) & 0xff; - sg_map_arr[j].target_id = m_idlun.dev_id & 0xff; - sg_map_arr[j].dev_name = devices[k]; - - printf("[scsi%d ch=%d id=%d lun=%d %s] ", sg_map_arr[j].bus, - sg_map_arr[j].channel, sg_map_arr[j].target_id, - sg_map_arr[j].lun, sg_map_arr[j].dev_name); - - ++j; - printf("%s ", devices[k]); - close(fd); - }; - printf("\n"); - for (k = 0; k < MAX_SG_DEVS; k++) { - make_dev_name(name, NULL, k, do_numeric); - fd = open(name, O_RDWR | O_NONBLOCK); - if (fd < 0) { - if ((ENOENT == errno) && (0 == k)) { - do_numeric = 0; - make_dev_name(name, NULL, k, do_numeric); - fd = open(name, O_RDWR | O_NONBLOCK); - } - if (fd < 0) { - if (EBUSY == errno) - continue; /* step over if O_EXCL already on it */ - else { -#if 0 - snprintf(ebuff, EBUFF_SZ, - "open on %s failed (%d)", name, - errno); - perror(ebuff); -#endif - if (max_holes-- > 0) - continue; - else - break; - } - } - } - max_holes = MAX_HOLES; - err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus); - if (err < 0) { - snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", - name); - perror(ebuff); - close(fd); - continue; - } - err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun); - if (err < 0) { - snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", - name); - perror(ebuff); - close(fd); - continue; - } - - printf("[scsi%d ch=%d id=%d lun=%d %s]", bus, - (m_idlun.dev_id >> 16) & 0xff, m_idlun.dev_id & 0xff, - (m_idlun.dev_id >> 8) & 0xff, name); - - for (j = 0; sg_map_arr[j].dev_name; ++j) { - if ((bus == sg_map_arr[j].bus) && - ((m_idlun.dev_id & 0xff) == sg_map_arr[j].target_id) - && (((m_idlun.dev_id >> 16) & 0xff) == - sg_map_arr[j].channel) - && (((m_idlun.dev_id >> 8) & 0xff) == - sg_map_arr[j].lun)) { - printf("%s [=%s scsi%d ch=%d id=%d lun=%d]\n", - name, sg_map_arr[j].dev_name, bus, - ((m_idlun.dev_id >> 16) & 0xff), - m_idlun.dev_id & 0xff, - ((m_idlun.dev_id >> 8) & 0xff)); - break; - } - } - if (NULL == sg_map_arr[j].dev_name) - printf("%s [scsi%d ch=%d id=%d lun=%d]\n", name, bus, - ((m_idlun.dev_id >> 16) & 0xff), - m_idlun.dev_id & 0xff, - ((m_idlun.dev_id >> 8) & 0xff)); - close(fd); - } - printf("\n"); -} - -static int show_pages(int page_code) -{ - int offset; - int length; - int i; - unsigned long long pages_sup = 0; - unsigned long long pages_mask = 0; - - if (!get_mode_page10(0x3f, page_code | 0x10)) { - length = 9 + getnbyte(buffer + 8, 2); - offset = 16 + getnbyte(buffer + 14, 2); - } else if (!get_mode_page(0x3f, page_code | 0x10)) { - length = 9 + buffer[8]; - offset = 12 + buffer[11]; - } else { /* Assume SCSI-1 and fake settings to report NO pages */ - offset = 10; - length = 0; - } - - /* Get mask of pages supported by prog: */ - for (i = 0; i < MAX_PAGENO; i++) - if (page_names[i]) - pages_mask |= (1LL << i); - - /* Get pages listed in mode_pages */ - while (offset < length) { - pages_sup |= (1LL << (buffer[offset] & 0x3f)); - offset += 2 + buffer[offset + 1]; - } - - /* Mask out pages unsupported by this binary */ - pages_sup &= pages_mask; - - /* Notch page supported? */ - if (pages_sup & (1LL << 12)) { - if (get_mode_page(12, 0)) - return 2; - offset = 12 + buffer[11]; - } else { /* Fake empty notch page */ - memset(buffer, 0, SIZEOF_BUFFER); - offset = 0; - } - - pages_mask = getnbyte(buffer + offset + 16, 4); - pages_mask <<= 32; - pages_mask += getnbyte(buffer + offset + 20, 4); - - puts("Mode Pages supported by this binary and target:"); - puts("-----------------------------------------------"); - for (i = 0; i < MAX_PAGENO; i++) - if (pages_sup & (1LL << i)) - printf("%02xh: %s Page%s\n", i, get_page_name(i), - (pages_mask & (1LL << i)) ? " (notched)" : ""); - if (pages_sup & (1LL << 12)) { - printf("\nCurrent notch is %d.\n", - getnbyte(buffer + offset + 6, 2)); - } - if (!pages_sup) - puts("No mode pages supported (SCSI-1?)."); - - return 0; -} - -static int open_sg_dev(char *devname) -{ - int fd, err, bus, bbus, k; - My_scsi_idlun m_idlun, mm_idlun; - int do_numeric = 1; - char name[DEVNAME_SZ]; - struct stat a_st; - int block_dev = 0; - - strncpy(name, devname, DEVNAME_SZ); - name[DEVNAME_SZ - 1] = '\0'; - fd = open(name, O_RDONLY); - if (fd < 0) - return fd; - if (fstat(fd, &a_st) < 0) { - fprintf(stderr, "could do fstat() on fd ??\n"); - close(fd); - return -9999; - } - if (S_ISBLK(a_st.st_mode)) - block_dev = 1; - if (block_dev || (ioctl(fd, SG_GET_TIMEOUT, 0) < 0)) { - err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus); - if (err < 0) { - perror("A SCSI device name is required\n"); - close(fd); - return -9999; - } - err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun); - if (err < 0) { - perror("A SCSI device name is required\n"); - close(fd); - return -9999; - } - close(fd); - - for (k = 0; k < MAX_SG_DEVS; k++) { - make_dev_name(name, NULL, k, do_numeric); - fd = open(name, O_RDWR | O_NONBLOCK); - if (fd < 0) { - if ((ENOENT == errno) && (0 == k)) { - do_numeric = 0; - make_dev_name(name, NULL, k, - do_numeric); - fd = open(name, O_RDWR | O_NONBLOCK); - } - if (fd < 0) { - if (EBUSY == errno) - continue; /* step over if O_EXCL already on it */ - else - break; - } - } - err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bbus); - if (err < 0) { - perror("sg ioctl failed"); - close(fd); - fd = -9999; - } - err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &mm_idlun); - if (err < 0) { - perror("sg ioctl failed"); - close(fd); - fd = -9999; - } - if ((bus == bbus) && - ((m_idlun.dev_id & 0xff) == - (mm_idlun.dev_id & 0xff)) - && (((m_idlun.dev_id >> 8) & 0xff) == - ((mm_idlun.dev_id >> 8) & 0xff)) - && (((m_idlun.dev_id >> 16) & 0xff) == - ((mm_idlun.dev_id >> 16) & 0xff))) - break; - else { - close(fd); - fd = -9999; - } - } - } - if (fd >= 0) { -#ifdef SG_GET_RESERVED_SIZE - int size; - - if (ioctl(fd, SG_GET_RESERVED_SIZE, &size) < 0) { - fprintf(stderr, - "Compiled with new driver, running on old!!\n"); - close(fd); - return -9999; - } -#endif - close(fd); - return open(name, O_RDWR); - } else - return fd; -} - -int show_scsi_info(char *device) -{ - int page_code = 0; - int status = 0; - - print_msg(TEST_BREAK, __FUNCTION__); - - show_devices(); - - glob_fd = open_sg_dev(device); - if (glob_fd < 0) { - if (-9999 == glob_fd) - fprintf(stderr, - "Couldn't find sg device corresponding to %s\n", - device); - else { - perror("sginfo(open)"); - fprintf(stderr, - "file=%s, or no corresponding sg device found\n", - device); - fprintf(stderr, "Is sg driver loaded?\n"); - } - return 1; - } - - status |= do_scsi_info_inquiry(page_code); - - status |= do_serial_number(page_code); - - status |= read_geometry(page_code); - - status |= read_cache(page_code); - - status |= read_format_info(page_code); - - status |= error_recovery_page(page_code); - - status |= read_control_page(page_code); - - status |= read_disconnect_reconnect_data(page_code); - - status |= read_defect_list(page_code); - - status |= notch_parameters_page(page_code); - - status |= verify_error_recovery(page_code); - - status |= peripheral_device_page(page_code); - - status |= do_user_page(page_code, 0); - - status |= show_pages(page_code); - - return status; -} - -/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), - 2 -> try again */ -int sg_read2(int sg_fd, unsigned char *buff, int blocks, int from_block, - int bs, int cdbsz, int fua, int do_mmap) -{ - unsigned char rdCmd[MAX_SCSI_CDBSZ]; - unsigned char senseBuff[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - int res; - - if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) { - fprintf(stderr, - ME "bad rd cdb build, from_block=%d, blocks=%d\n", - from_block, blocks); - return -1; - } - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = cdbsz; - io_hdr.cmdp = rdCmd; - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = bs * blocks; - if (!do_mmap) - io_hdr.dxferp = buff; - io_hdr.mx_sb_len = SENSE_BUFF_LEN; - io_hdr.sbp = senseBuff; - io_hdr.timeout = DEF_TIMEOUT; - io_hdr.pack_id = from_block; - if (do_mmap) - io_hdr.flags |= SG_FLAG_MMAP_IO; - - while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && - (EINTR == errno)) ; - if (res < 0) { - if (ENOMEM == errno) - return 1; - perror("reading (wr) on sg device, error"); - return -1; - } - - while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && - (EINTR == errno)) ; - if (res < 0) { - perror("reading (rd) on sg device, error"); - return -1; - } - switch (sg_err_category3(&io_hdr)) { - case SG_ERR_CAT_CLEAN: - break; - case SG_ERR_CAT_RECOVERED: - fprintf(stderr, - "Recovered error while reading block=%d, num=%d\n", - from_block, blocks); - break; - case SG_ERR_CAT_MEDIA_CHANGED: - return 2; - default: - sg_chk_n_print3("reading", &io_hdr); - return -1; - } - sum_of_resids += io_hdr.resid; -#if SG_DEBUG - fprintf(stderr, "duration=%u ms\n", io_hdr.duration); -#endif - return 0; -} - -/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM), - 2 -> try again */ -int sg_write2(int sg_fd, unsigned char *buff, int blocks, int to_block, - int bs, int cdbsz, int fua, int do_mmap, int *diop) -{ - unsigned char wrCmd[MAX_SCSI_CDBSZ]; - unsigned char senseBuff[SENSE_BUFF_LEN]; - sg_io_hdr_t io_hdr; - int res; - - if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) { - fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n", - to_block, blocks); - return -1; - } - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = cdbsz; - io_hdr.cmdp = wrCmd; - io_hdr.dxfer_direction = SG_DXFER_TO_DEV; - io_hdr.dxfer_len = bs * blocks; - if (!do_mmap) - io_hdr.dxferp = buff; - io_hdr.mx_sb_len = SENSE_BUFF_LEN; - io_hdr.sbp = senseBuff; - io_hdr.timeout = DEF_TIMEOUT; - io_hdr.pack_id = to_block; - if (do_mmap) - io_hdr.flags |= SG_FLAG_MMAP_IO; - if (diop && *diop) - io_hdr.flags |= SG_FLAG_DIRECT_IO; - - while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && - (EINTR == errno)) ; - if (res < 0) { - if (ENOMEM == errno) - return 1; - perror("writing (wr) on sg device, error"); - return -1; - } - - while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) && - (EINTR == errno)) ; - if (res < 0) { - perror("writing (rd) on sg device, error"); - return -1; - } - switch (sg_err_category3(&io_hdr)) { - case SG_ERR_CAT_CLEAN: - break; - case SG_ERR_CAT_RECOVERED: - fprintf(stderr, - "Recovered error while writing block=%d, num=%d\n", - to_block, blocks); - break; - case SG_ERR_CAT_MEDIA_CHANGED: - return 2; - default: - sg_chk_n_print3("writing", &io_hdr); - return -1; - } - if (diop && *diop && - ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) - *diop = 0; /* flag that dio not done (completely) */ - return 0; -} - -int do_scsi_sgm_read_write(char *device) -{ - int skip = 0; - int seek = 0; - int bs = 0; - int bpt = DEF_BLOCKS_PER_TRANSFER; - char inf[INOUTF_SZ]; - int in_type = FT_OTHER; - char outf[INOUTF_SZ]; - int out_type = FT_OTHER; - int res, t; - int infd, outfd, blocks; - unsigned char *wrkPos; - unsigned char *wrkBuff = NULL; - unsigned char *wrkMmap = NULL; - int in_num_sect = 0; - int in_res_sz = 0; - int out_num_sect = 0; - int out_res_sz = 0; - int do_time = 1; - int scsi_cdbsz = DEF_SCSI_CDBSZ; - int do_sync = 1; - int do_dio = 0; - int num_dio_not_done = 0; - int fua_mode = 0; - int in_sect_sz, out_sect_sz; - char ebuff[EBUFF_SZ]; - int blocks_per; - int req_count; - size_t psz = getpagesize(); - struct timeval start_tm, end_tm; - - print_msg(TEST_BREAK, __FUNCTION__); - - strcpy(inf, "/dev/zero"); - strcpy(outf, device); - - install_handler(SIGINT, interrupt_handler); - install_handler(SIGQUIT, interrupt_handler); - install_handler(SIGPIPE, interrupt_handler); - install_handler(SIGUSR1, siginfo_handler); - - infd = STDIN_FILENO; - outfd = STDOUT_FILENO; - - in_type = dd_filetype(inf); - - if (FT_ST == in_type) { - fprintf(stderr, ME "unable to use scsi tape device %s\n", inf); - return 1; - } else if (FT_SG == in_type) { - if ((infd = open(inf, O_RDWR)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for sg reading", inf); - perror(ebuff); - return 1; - } - res = ioctl(infd, SG_GET_VERSION_NUM, &t); - if ((res < 0) || (t < 30122)) { - fprintf(stderr, ME "sg driver prior to 3.1.22\n"); - return 1; - } - in_res_sz = bs * bpt; - if (0 != (in_res_sz % psz)) /* round up to next page */ - in_res_sz = ((in_res_sz / psz) + 1) * psz; - if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) { - perror(ME "SG_GET_RESERVED_SIZE error"); - return 1; - } - if (in_res_sz > t) { - if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) { - perror(ME "SG_SET_RESERVED_SIZE error"); - return 1; - } - } - wrkMmap = mmap(NULL, in_res_sz, PROT_READ | PROT_WRITE, - MAP_SHARED, infd, 0); - if (MAP_FAILED == wrkMmap) { - snprintf(ebuff, EBUFF_SZ, - ME "error using mmap() on file: %s", inf); - perror(ebuff); - return 1; - } - } else { - if ((infd = open(inf, O_RDONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for reading", inf); - perror(ebuff); - return 1; - } else if (skip > 0) { - llse_loff_t offset = skip; - - offset *= bs; /* could exceed 32 bits here! */ - if (llse_llseek(infd, offset, SEEK_SET) < 0) { - snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to " - "required position on %s", inf); - perror(ebuff); - return 1; - } - } - } - - if (outf[0] && ('-' != outf[0])) { - out_type = dd_filetype(outf); - - if (FT_ST == out_type) { - fprintf(stderr, - ME "unable to use scsi tape device %s\n", outf); - return 1; - } else if (FT_SG == out_type) { - if ((outfd = open(outf, O_RDWR)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for " - "sg writing", outf); - perror(ebuff); - return 1; - } - res = ioctl(outfd, SG_GET_VERSION_NUM, &t); - if ((res < 0) || (t < 30122)) { - fprintf(stderr, - ME "sg driver prior to 3.1.22\n"); - return 1; - } - if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) { - perror(ME "SG_GET_RESERVED_SIZE error"); - return 1; - } - out_res_sz = bs * bpt; - if (out_res_sz > t) { - if (ioctl - (outfd, SG_SET_RESERVED_SIZE, - &out_res_sz) < 0) { - perror(ME "SG_SET_RESERVED_SIZE error"); - return 1; - } - } - if (NULL == wrkMmap) { - wrkMmap = - mmap(NULL, out_res_sz, - PROT_READ | PROT_WRITE, MAP_SHARED, - outfd, 0); - if (MAP_FAILED == wrkMmap) { - snprintf(ebuff, EBUFF_SZ, - ME - "error using mmap() on file: %s", - outf); - perror(ebuff); - return 1; - } - } - } else if (FT_DEV_NULL == out_type) - outfd = -1; /* don't bother opening */ - else { - if (FT_RAW != out_type) { - if ((outfd = - open(outf, O_WRONLY | O_CREAT, - 0666)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "could not open %s for writing", - outf); - perror(ebuff); - return 1; - } - } else { - if ((outfd = open(outf, O_WRONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s " - "for raw writing", outf); - perror(ebuff); - return 1; - } - } - if (seek > 0) { - llse_loff_t offset = seek; - - offset *= bs; /* could exceed 32 bits here! */ - if (llse_llseek(outfd, offset, SEEK_SET) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "couldn't seek to " - "required position on %s", - outf); - perror(ebuff); - return 1; - } - } - } - } - if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) { - fprintf(stderr, - "Can't have both 'if' as stdin _and_ 'of' as stdout\n"); - return 1; - } -#if 0 - if ((FT_OTHER == in_type) && (FT_OTHER == out_type)) { - fprintf(stderr, "Both 'if' and 'of' can't be ordinary files\n"); - return 1; - } -#endif - if (dd_count < 0) { - if (FT_SG == in_type) { - res = read_capacity(infd, &in_num_sect, &in_sect_sz); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(in), continuing\n"); - res = - read_capacity(infd, &in_num_sect, - &in_sect_sz); - } - if (0 != res) { - fprintf(stderr, - "Unable to read capacity on %s\n", inf); - in_num_sect = -1; - } else { -#if 0 - if (0 == in_sect_sz) - in_sect_sz = bs; - else if (in_sect_sz > bs) - in_num_sect *= (in_sect_sz / bs); - else if (in_sect_sz < bs) - in_num_sect /= (bs / in_sect_sz); -#endif - if (in_num_sect > skip) - in_num_sect -= skip; - } - } - if (FT_SG == out_type) { - res = read_capacity(outfd, &out_num_sect, &out_sect_sz); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(out), continuing\n"); - res = - read_capacity(outfd, &out_num_sect, - &out_sect_sz); - } - if (0 != res) { - fprintf(stderr, - "Unable to read capacity on %s\n", - outf); - out_num_sect = -1; - } else { - if (out_num_sect > seek) - out_num_sect -= seek; - } - } -#ifdef SG_DEBUG - fprintf(stderr, - "Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n", - dd_count, in_num_sect, out_num_sect); -#endif - if (in_num_sect > 0) { - if (out_num_sect > 0) - dd_count = - (in_num_sect > - out_num_sect) ? out_num_sect : in_num_sect; - else - dd_count = in_num_sect; - } else - dd_count = out_num_sect; - } - if (dd_count < 0) { - fprintf(stderr, "Couldn't calculate count, please give one\n"); - return 1; - } - if (do_dio && (FT_SG != in_type)) { - do_dio = 0; - fprintf(stderr, - ">>> dio only performed on 'of' side when 'if' is" - " an sg device\n"); - } - if (do_dio) { - int fd; - char c; - - if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { - if (1 == read(fd, &c, 1)) { - if ('0' == c) - fprintf(stderr, - ">>> %s set to '0' but should be set " - "to '1' for direct IO\n", - proc_allow_dio); - } - close(fd); - } - } - - if (wrkMmap) - wrkPos = wrkMmap; - else { - if ((FT_RAW == in_type) || (FT_RAW == out_type)) { - wrkBuff = malloc(bs * bpt + psz); - if (0 == wrkBuff) { - fprintf(stderr, - "Not enough user memory for raw\n"); - return 1; - } - wrkPos = - (unsigned char *)(((unsigned long)wrkBuff + psz - 1) - & (~(psz - 1))); - } else { - wrkBuff = malloc(bs * bpt); - if (0 == wrkBuff) { - fprintf(stderr, "Not enough user memory\n"); - return 1; - } - wrkPos = wrkBuff; - } - } - - blocks_per = bpt; -#ifdef SG_DEBUG - fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n", - dd_count, blocks_per); -#endif - if (do_time) { - start_tm.tv_sec = 0; - start_tm.tv_usec = 0; - gettimeofday(&start_tm, NULL); - } - req_count = dd_count; - - while (dd_count > 0) { - blocks = (dd_count > blocks_per) ? blocks_per : dd_count; - if (FT_SG == in_type) { - int fua = fua_mode & 2; - - res = - sg_read2(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, - fua, 1); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed, continuing (r)\n"); - res = - sg_read2(infd, wrkPos, blocks, skip, bs, - scsi_cdbsz, fua, 1); - } - if (0 != res) { - fprintf(stderr, "sg_read2 failed, skip=%d\n", - skip); - break; - } else - in_full += blocks; - } else { - while (((res = read(infd, wrkPos, blocks * bs)) < 0) && - (EINTR == errno)) ; - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "reading, skip=%d ", skip); - perror(ebuff); - break; - } else if (res < blocks * bs) { - dd_count = 0; - blocks = res / bs; - if ((res % bs) > 0) { - blocks++; - in_partial++; - } - } - in_full += blocks; - } - - if (FT_SG == out_type) { - int do_mmap = (FT_SG == in_type) ? 0 : 1; - int fua = fua_mode & 1; - int dio_res = do_dio; - - res = - sg_write2(outfd, wrkPos, blocks, seek, bs, - scsi_cdbsz, fua, do_mmap, &dio_res); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed, continuing (w)\n"); - res = - sg_write2(outfd, wrkPos, blocks, seek, bs, - scsi_cdbsz, fua, do_mmap, - &dio_res); - } else if (0 != res) { - fprintf(stderr, "sg_write2 failed, seek=%d\n", - seek); - break; - } else { - out_full += blocks; - if (do_dio && (0 == dio_res)) - num_dio_not_done++; - } - } else if (FT_DEV_NULL == out_type) - out_full += blocks; /* act as if written out without error */ - else { - while (((res = write(outfd, wrkPos, blocks * bs)) < 0) - && (EINTR == errno)) ; - if (res < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "writing, seek=%d ", seek); - perror(ebuff); - break; - } else if (res < blocks * bs) { - fprintf(stderr, - "output file probably full, seek=%d ", - seek); - blocks = res / bs; - out_full += blocks; - if ((res % bs) > 0) - out_partial++; - break; - } else - out_full += blocks; - } - if (dd_count > 0) - dd_count -= blocks; - skip += blocks; - seek += blocks; - } - if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { - struct timeval res_tm; - double a, b; - - gettimeofday(&end_tm, NULL); - res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; - res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; - if (res_tm.tv_usec < 0) { - --res_tm.tv_sec; - res_tm.tv_usec += 1000000; - } - a = res_tm.tv_sec; - a += (0.000001 * res_tm.tv_usec); - b = (double)bs *(req_count - dd_count); - printf("time to transfer data was %d.%06d secs", - (int)res_tm.tv_sec, (int)res_tm.tv_usec); - if ((a > 0.00001) && (b > 511)) - printf(", %.2f MB/sec\n", b / (a * 1000000.0)); - else - printf("\n"); - } - if (do_sync) { - if (FT_SG == out_type) { - fprintf(stderr, ">> Synchronizing cache on %s\n", outf); - res = sync_cache(outfd); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(in), continuing\n"); - res = sync_cache(outfd); - } - if (0 != res) - fprintf(stderr, - "Unable to synchronize cache\n"); - } - } - - if (wrkBuff) - free(wrkBuff); - if (STDIN_FILENO != infd) - close(infd); - if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type)) - close(outfd); - res = 0; - if (0 != dd_count) { - fprintf(stderr, "Some error occurred,"); - res = 2; - } - print_stats(); - if (sum_of_resids) - fprintf(stderr, ">> Non-zero sum of residual counts=%d\n", - sum_of_resids); - if (num_dio_not_done) - fprintf(stderr, ">> dio requested but _not done %d times\n", - num_dio_not_done); - return res; -} - -static void guarded_stop_in(Rq_coll * clp) -{ - pthread_mutex_lock(&clp->in_mutex); - clp->in_stop = 1; - pthread_mutex_unlock(&clp->in_mutex); -} - -static void guarded_stop_out(Rq_coll * clp) -{ - pthread_mutex_lock(&clp->out_mutex); - clp->out_stop = 1; - pthread_mutex_unlock(&clp->out_mutex); -} - -static void guarded_stop_both(Rq_coll * clp) -{ - guarded_stop_in(clp); - guarded_stop_out(clp); -} - -void *sig_listen_thread(void *v_clp) -{ - Rq_coll *clp = (Rq_coll *) v_clp; - int sig_number; - - while (1) { - sigwait(&signal_set, &sig_number); - if (SIGINT == sig_number) { - fprintf(stderr, ME "interrupted by SIGINT\n"); - guarded_stop_both(clp); - pthread_cond_broadcast(&clp->out_sync_cv); - } - } - return NULL; -} - -void cleanup_in(void *v_clp) -{ - Rq_coll *clp = (Rq_coll *) v_clp; - - fprintf(stderr, "thread cancelled while in mutex held\n"); - clp->in_stop = 1; - pthread_mutex_unlock(&clp->in_mutex); - guarded_stop_out(clp); - pthread_cond_broadcast(&clp->out_sync_cv); -} - -void cleanup_out(void *v_clp) -{ - Rq_coll *clp = (Rq_coll *) v_clp; - - fprintf(stderr, "thread cancelled while out mutex held\n"); - clp->out_stop = 1; - pthread_mutex_unlock(&clp->out_mutex); - guarded_stop_in(clp); - pthread_cond_broadcast(&clp->out_sync_cv); -} - -void *read_write_thread(void *v_clp) -{ - Rq_coll *clp = (Rq_coll *) v_clp; - Rq_elem rel; - Rq_elem *rep = &rel; - size_t psz = 0; - int sz = clp->bpt * clp->bs; - int stop_after_write = 0; - int seek_skip = clp->seek - clp->skip; - int blocks, status; - - memset(rep, 0, sizeof(Rq_elem)); - psz = getpagesize(); - if (NULL == (rep->alloc_bp = malloc(sz + psz))) - err_exit(ENOMEM, "out of memory creating user buffers\n"); - rep->buffp = - (unsigned char *)(((unsigned long)rep->alloc_bp + psz - 1) & - (~(psz - 1))); - /* Follow clp members are constant during lifetime of thread */ - rep->bs = clp->bs; - rep->fua_mode = clp->fua_mode; - rep->dio = clp->dio; - rep->infd = clp->infd; - rep->outfd = clp->outfd; - rep->debug = clp->debug; - rep->in_scsi_type = clp->in_scsi_type; - rep->out_scsi_type = clp->out_scsi_type; - rep->cdbsz = clp->cdbsz; - - while (1) { - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) - err_exit(status, "lock in_mutex"); - if (clp->in_stop || (clp->in_count <= 0)) { - /* no more to do, exit loop then thread */ - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) - err_exit(status, "unlock in_mutex"); - break; - } - blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count; - rep->wr = 0; - rep->blk = clp->in_blk; - rep->num_blks = blocks; - clp->in_blk += blocks; - clp->in_count -= blocks; - - pthread_cleanup_push(cleanup_in, (void *)clp); - if (FT_SG == clp->in_type) - sg_in_operation(clp, rep); /* lets go of in_mutex mid operation */ - else { - stop_after_write = - normal_in_operation(clp, rep, blocks); - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) - err_exit(status, "unlock in_mutex"); - } - pthread_cleanup_pop(0); - - status = pthread_mutex_lock(&clp->out_mutex); - if (0 != status) - err_exit(status, "lock out_mutex"); - if (FT_DEV_NULL != clp->out_type) { - while ((!clp->out_stop) && - ((rep->blk + seek_skip) != clp->out_blk)) { - /* if write would be out of sequence then wait */ - pthread_cleanup_push(cleanup_out, (void *)clp); - status = - pthread_cond_wait(&clp->out_sync_cv, - &clp->out_mutex); - if (0 != status) - err_exit(status, "cond out_sync_cv"); - pthread_cleanup_pop(0); - } - } - - if (clp->out_stop || (clp->out_count <= 0)) { - if (!clp->out_stop) - clp->out_stop = 1; - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) - err_exit(status, "unlock out_mutex"); - break; - } - if (stop_after_write) - clp->out_stop = 1; - rep->wr = 1; - rep->blk = clp->out_blk; - /* rep->num_blks = blocks; */ - clp->out_blk += blocks; - clp->out_count -= blocks; - - pthread_cleanup_push(cleanup_out, (void *)clp); - if (FT_SG == clp->out_type) - sg_out_operation(clp, rep); /* releases out_mutex mid operation */ - else if (FT_DEV_NULL == clp->out_type) { - /* skip actual write operation */ - clp->out_done_count -= blocks; - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) - err_exit(status, "unlock out_mutex"); - } else { - normal_out_operation(clp, rep, blocks); - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) - err_exit(status, "unlock out_mutex"); - } - pthread_cleanup_pop(0); - - if (stop_after_write) - break; - pthread_cond_broadcast(&clp->out_sync_cv); - } /* end of while loop */ - if (rep->alloc_bp) - free(rep->alloc_bp); - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) - err_exit(status, "lock in_mutex"); - if (!clp->in_stop) - clp->in_stop = 1; /* flag other workers to stop */ - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) - err_exit(status, "unlock in_mutex"); - pthread_cond_broadcast(&clp->out_sync_cv); - return stop_after_write ? NULL : v_clp; -} - -int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks) -{ - int res; - int stop_after_write = 0; - - /* enters holding in_mutex */ - while (((res = read(clp->infd, rep->buffp, - blocks * clp->bs)) < 0) && (EINTR == errno)) ; - if (res < 0) { - if (clp->coe) { - memset(rep->buffp, 0, rep->num_blks * rep->bs); - fprintf(stderr, - ">> substituted zeros for in blk=%d for " - "%d bytes, %s\n", rep->blk, - rep->num_blks * rep->bs, strerror(errno)); - res = rep->num_blks * clp->bs; - } else { - fprintf(stderr, "error in normal read, %s\n", - strerror(errno)); - clp->in_stop = 1; - guarded_stop_out(clp); - return 1; - } - } - if (res < blocks * clp->bs) { - int o_blocks = blocks; - stop_after_write = 1; - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { - blocks++; - clp->in_partial++; - } - /* Reverse out + re-apply blocks on clp */ - clp->in_blk -= o_blocks; - clp->in_count += o_blocks; - rep->num_blks = blocks; - clp->in_blk += blocks; - clp->in_count -= blocks; - } - clp->in_done_count -= blocks; - return stop_after_write; -} - -void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks) -{ - int res; - - /* enters holding out_mutex */ - while (((res = write(clp->outfd, rep->buffp, - rep->num_blks * clp->bs)) < 0) - && (EINTR == errno)) ; - if (res < 0) { - if (clp->coe) { - fprintf(stderr, ">> ignored error for out blk=%d for " - "%d bytes, %s\n", rep->blk, - rep->num_blks * rep->bs, strerror(errno)); - res = rep->num_blks * clp->bs; - } else { - fprintf(stderr, "error normal write, %s\n", - strerror(errno)); - guarded_stop_in(clp); - clp->out_stop = 1; - return; - } - } - if (res < blocks * clp->bs) { - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { - blocks++; - clp->out_partial++; - } - rep->num_blks = blocks; - } - clp->out_done_count -= blocks; -} - -void sg_in_operation(Rq_coll * clp, Rq_elem * rep) -{ - int res; - int status; - - /* enters holding in_mutex */ - while (1) { - res = sg_start_io(rep); - if (1 == res) - err_exit(ENOMEM, "sg starting in command"); - else if (res < 0) { - fprintf(stderr, ME "inputting to sg failed, blk=%d\n", - rep->blk); - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) - err_exit(status, "unlock in_mutex"); - guarded_stop_both(clp); - return; - } - /* Now release in mutex to let other reads run in parallel */ - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) - err_exit(status, "unlock in_mutex"); - - res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); - if (res < 0) { - if (clp->coe) { - memset(rep->buffp, 0, rep->num_blks * rep->bs); - fprintf(stderr, - ">> substituted zeros for in blk=%d for " - "%d bytes\n", rep->blk, - rep->num_blks * rep->bs); - } else { - fprintf(stderr, - "error finishing sg in command\n"); - guarded_stop_both(clp); - return; - } - } - if (res <= 0) { /* looks good, going to return */ - if (rep->dio_incomplete || rep->resid) { - status = pthread_mutex_lock(&clp->aux_mutex); - if (0 != status) - err_exit(status, "lock aux_mutex"); - clp->dio_incomplete += rep->dio_incomplete; - clp->sum_of_resids += rep->resid; - status = pthread_mutex_unlock(&clp->aux_mutex); - if (0 != status) - err_exit(status, "unlock aux_mutex"); - } - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) - err_exit(status, "lock in_mutex"); - clp->in_done_count -= rep->num_blks; - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) - err_exit(status, "unlock in_mutex"); - return; - } - /* else assume 1 == res so try again with same addr, count info */ - /* now re-acquire read mutex for balance */ - /* N.B. This re-read could now be out of read sequence */ - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) - err_exit(status, "lock in_mutex"); - } -} - -void sg_out_operation(Rq_coll * clp, Rq_elem * rep) -{ - int res; - int status; - - /* enters holding out_mutex */ - while (1) { - res = sg_start_io(rep); - if (1 == res) - err_exit(ENOMEM, "sg starting out command"); - else if (res < 0) { - fprintf(stderr, - ME "outputting from sg failed, blk=%d\n", - rep->blk); - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) - err_exit(status, "unlock out_mutex"); - guarded_stop_both(clp); - return; - } - /* Now release in mutex to let other reads run in parallel */ - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) - err_exit(status, "unlock out_mutex"); - - res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); - if (res < 0) { - if (clp->coe) - fprintf(stderr, - ">> ignored error for out blk=%d for " - "%d bytes\n", rep->blk, - rep->num_blks * rep->bs); - else { - fprintf(stderr, - "error finishing sg out command\n"); - guarded_stop_both(clp); - return; - } - } - if (res <= 0) { - if (rep->dio_incomplete || rep->resid) { - status = pthread_mutex_lock(&clp->aux_mutex); - if (0 != status) - err_exit(status, "lock aux_mutex"); - clp->dio_incomplete += rep->dio_incomplete; - clp->sum_of_resids += rep->resid; - status = pthread_mutex_unlock(&clp->aux_mutex); - if (0 != status) - err_exit(status, "unlock aux_mutex"); - } - status = pthread_mutex_lock(&clp->out_mutex); - if (0 != status) - err_exit(status, "lock out_mutex"); - clp->out_done_count -= rep->num_blks; - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) - err_exit(status, "unlock out_mutex"); - return; - } - /* else assume 1 == res so try again with same addr, count info */ - /* now re-acquire out mutex for balance */ - /* N.B. This re-write could now be out of write sequence */ - status = pthread_mutex_lock(&clp->out_mutex); - if (0 != status) - err_exit(status, "lock out_mutex"); - } -} - -int sg_start_io(Rq_elem * rep) -{ - sg_io_hdr_t *hp = &rep->io_hdr; - int fua = rep->wr ? (rep->fua_mode & 1) : (rep->fua_mode & 2); - int res; - - if (sg_build_scsi_cdb(rep->cmd, rep->cdbsz, rep->num_blks, rep->blk, - rep->wr, fua, 0)) { - fprintf(stderr, ME "bad cdb build, start_blk=%d, blocks=%d\n", - rep->blk, rep->num_blks); - return -1; - } - memset(hp, 0, sizeof(sg_io_hdr_t)); - hp->interface_id = 'S'; - hp->cmd_len = rep->cdbsz; - hp->cmdp = rep->cmd; - hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; - hp->dxfer_len = rep->bs * rep->num_blks; - hp->dxferp = rep->buffp; - hp->mx_sb_len = sizeof(rep->sb); - hp->sbp = rep->sb; - hp->timeout = DEF_TIMEOUT; - hp->usr_ptr = rep; - hp->pack_id = rep->blk; - if (rep->dio) - hp->flags |= SG_FLAG_DIRECT_IO; - if (rep->debug > 8) { - fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n", - rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); - sg_print_command(hp->cmdp); - fprintf(stderr, "dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n", - hp->dxfer_direction, hp->dxfer_len, hp->dxferp, - hp->cmd_len); - } - - while (((res = write(rep->wr ? rep->outfd : rep->infd, hp, - sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ; - if (res < 0) { - if (ENOMEM == errno) - return 1; - perror("starting io on sg device, error"); - return -1; - } - return 0; -} - -/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */ -int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp) -{ - int res, status; - sg_io_hdr_t io_hdr; - sg_io_hdr_t *hp; -#if 0 - static int testing = 0; /* thread dubious! */ -#endif - - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - /* FORCE_PACK_ID active set only read packet with matching pack_id */ - io_hdr.interface_id = 'S'; - io_hdr.dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; - io_hdr.pack_id = rep->blk; - - while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr, - sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ; - if (res < 0) { - perror("finishing io on sg device, error"); - return -1; - } - if (rep != (Rq_elem *) io_hdr.usr_ptr) - err_exit(0, - "sg_finish_io: bad usr_ptr, request-response mismatch\n"); - memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t)); - hp = &rep->io_hdr; - - switch (sg_err_category3(hp)) { - case SG_ERR_CAT_CLEAN: - break; - case SG_ERR_CAT_RECOVERED: - fprintf(stderr, "Recovered error on block=%d, num=%d\n", - rep->blk, rep->num_blks); - break; - case SG_ERR_CAT_MEDIA_CHANGED: - return 1; - default: - { - char ebuff[EBUFF_SZ]; - - snprintf(ebuff, EBUFF_SZ, - "%s blk=%d", rep->wr ? "writing" : "reading", - rep->blk); - status = pthread_mutex_lock(a_mutp); - if (0 != status) - err_exit(status, "lock aux_mutex"); - sg_chk_n_print3(ebuff, hp); - status = pthread_mutex_unlock(a_mutp); - if (0 != status) - err_exit(status, "unlock aux_mutex"); - return -1; - } - } -#if 0 - if (0 == (++testing % 100)) - return -1; -#endif - if (rep->dio && - ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)) - rep->dio_incomplete = 1; /* count dios done as indirect IO */ - else - rep->dio_incomplete = 0; - rep->resid = hp->resid; - if (rep->debug > 8) - fprintf(stderr, "sg_finish_io: completed %s\n", - wr ? "WRITE" : "READ"); - return 0; -} - -int sg_prepare(int fd, int bs, int bpt, int *scsi_typep) -{ - int res, t; - - res = ioctl(fd, SG_GET_VERSION_NUM, &t); - if ((res < 0) || (t < 30000)) { - fprintf(stderr, ME "sg driver prior to 3.x.y\n"); - return 1; - } - res = 0; - t = bs * bpt; - res = ioctl(fd, SG_SET_RESERVED_SIZE, &t); - if (res < 0) - perror(ME "SG_SET_RESERVED_SIZE error"); - t = 1; - res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t); - if (res < 0) - perror(ME "SG_SET_FORCE_PACK_ID error"); - if (scsi_typep) { - struct sg_scsi_id info; - - res = ioctl(fd, SG_GET_SCSI_ID, &info); - if (res < 0) - perror(ME "SG_SET_SCSI_ID error"); - *scsi_typep = info.scsi_type; - } - return 0; -} - -int do_scsi_sgp_read_write(char *device) -{ - int skip = 0; - int seek = 0; - int count = -1; - char inf[INOUTF_SZ]; - char outf[INOUTF_SZ]; - int res, k; - int in_num_sect = 0; - int out_num_sect = 0; - int num_threads = DEF_NUM_THREADS; - pthread_t threads[MAX_NUM_THREADS]; - int do_time = 1; - int do_sync = 1; - int in_sect_sz, out_sect_sz, status, infull, outfull; - void *vp; - char ebuff[EBUFF_SZ]; - struct timeval start_tm, end_tm; - Rq_coll rcoll; - - print_msg(TEST_BREAK, __FUNCTION__); - - memset(&rcoll, 0, sizeof(Rq_coll)); - rcoll.bpt = DEF_BLOCKS_PER_TRANSFER; - rcoll.in_type = FT_OTHER; - rcoll.out_type = FT_OTHER; - rcoll.cdbsz = DEF_SCSI_CDBSZ; - - strcpy(inf, "/dev/zero"); - strcpy(outf, device); - - if (rcoll.bs <= 0) { - rcoll.bs = DEF_BLOCK_SIZE; - fprintf(stderr, - "Assume default 'bs' (block size) of %d bytes\n", - rcoll.bs); - } - - if (rcoll.debug) - fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n", - inf, skip, outf, seek, count); - - rcoll.infd = STDIN_FILENO; - rcoll.outfd = STDOUT_FILENO; - if (inf[0] && ('-' != inf[0])) { - rcoll.in_type = dd_filetype(inf); - - if (FT_ST == rcoll.in_type) { - fprintf(stderr, - ME "unable to use scsi tape device %s\n", inf); - return 1; - } else if (FT_SG == rcoll.in_type) { - if ((rcoll.infd = open(inf, O_RDWR)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for sg reading", - inf); - perror(ebuff); - return 1; - } - if (sg_prepare(rcoll.infd, rcoll.bs, rcoll.bpt, - &rcoll.in_scsi_type)) - return 1; - } else { - if ((rcoll.infd = open(inf, O_RDONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for reading", - inf); - perror(ebuff); - return 1; - } else if (skip > 0) { - llse_loff_t offset = skip; - - offset *= rcoll.bs; /* could exceed 32 here! */ - if (llse_llseek(rcoll.infd, offset, SEEK_SET) < - 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "couldn't skip to required position on %s", - inf); - perror(ebuff); - return 1; - } - } - } - } - if (outf[0] && ('-' != outf[0])) { - rcoll.out_type = dd_filetype(outf); - - if (FT_ST == rcoll.out_type) { - fprintf(stderr, - ME "unable to use scsi tape device %s\n", outf); - return 1; - } else if (FT_SG == rcoll.out_type) { - if ((rcoll.outfd = open(outf, O_RDWR)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME "could not open %s for sg writing", - outf); - perror(ebuff); - return 1; - } - - if (sg_prepare(rcoll.outfd, rcoll.bs, rcoll.bpt, - &rcoll.out_scsi_type)) - return 1; - } else if (FT_DEV_NULL == rcoll.out_type) - rcoll.outfd = -1; /* don't bother opening */ - else { - if (FT_RAW != rcoll.out_type) { - if ((rcoll.outfd = - open(outf, O_WRONLY | O_CREAT, - 0666)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "could not open %s for writing", - outf); - perror(ebuff); - return 1; - } - } else { - if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "could not open %s for raw writing", - outf); - perror(ebuff); - return 1; - } - } - if (seek > 0) { - llse_loff_t offset = seek; - - offset *= rcoll.bs; /* could exceed 32 bits here! */ - if (llse_llseek(rcoll.outfd, offset, SEEK_SET) < - 0) { - snprintf(ebuff, EBUFF_SZ, - ME - "couldn't seek to required position on %s", - outf); - perror(ebuff); - return 1; - } - } - } - } - if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) { - fprintf(stderr, - "Disallow both if and of to be stdin and stdout"); - return 1; - } - if (count < 0) { - if (FT_SG == rcoll.in_type) { - res = - read_capacity(rcoll.infd, &in_num_sect, - &in_sect_sz); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(in), continuing\n"); - res = - read_capacity(rcoll.infd, &in_num_sect, - &in_sect_sz); - } - if (0 != res) { - fprintf(stderr, - "Unable to read capacity on %s\n", inf); - in_num_sect = -1; - } else { - if (in_num_sect > skip) - in_num_sect -= skip; - } - } - if (FT_SG == rcoll.out_type) { - res = - read_capacity(rcoll.outfd, &out_num_sect, - &out_sect_sz); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(out), continuing\n"); - res = - read_capacity(rcoll.outfd, &out_num_sect, - &out_sect_sz); - } - if (0 != res) { - fprintf(stderr, - "Unable to read capacity on %s\n", - outf); - out_num_sect = -1; - } else { - if (out_num_sect > seek) - out_num_sect -= seek; - } - } - if (in_num_sect > 0) { - if (out_num_sect > 0) - count = - (in_num_sect > - out_num_sect) ? out_num_sect : in_num_sect; - else - count = in_num_sect; - } else - count = out_num_sect; - } - if (rcoll.debug > 1) - fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, " - "out_num_sect=%d\n", count, in_num_sect, out_num_sect); - if (count < 0) { - fprintf(stderr, "Couldn't calculate count, please give one\n"); - return 1; - } - - rcoll.in_count = count; - rcoll.in_done_count = count; - rcoll.skip = skip; - rcoll.in_blk = skip; - rcoll.out_count = count; - rcoll.out_done_count = count; - rcoll.seek = seek; - rcoll.out_blk = seek; - status = pthread_mutex_init(&rcoll.in_mutex, NULL); - if (0 != status) - err_exit(status, "init in_mutex"); - status = pthread_mutex_init(&rcoll.out_mutex, NULL); - if (0 != status) - err_exit(status, "init out_mutex"); - status = pthread_mutex_init(&rcoll.aux_mutex, NULL); - if (0 != status) - err_exit(status, "init aux_mutex"); - status = pthread_cond_init(&rcoll.out_sync_cv, NULL); - if (0 != status) - err_exit(status, "init out_sync_cv"); - - sigemptyset(&signal_set); - sigaddset(&signal_set, SIGINT); - status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL); - if (0 != status) - err_exit(status, "pthread_sigmask"); - status = pthread_create(&sig_listen_thread_id, NULL, - sig_listen_thread, (void *)&rcoll); - if (0 != status) - err_exit(status, "pthread_create, sig..."); - - if (do_time) { - start_tm.tv_sec = 0; - start_tm.tv_usec = 0; - gettimeofday(&start_tm, NULL); - } - -/* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */ - if ((rcoll.out_done_count > 0) && (num_threads > 0)) { - /* Run 1 work thread to shake down infant retryable stuff */ - status = pthread_mutex_lock(&rcoll.out_mutex); - if (0 != status) - err_exit(status, "lock out_mutex"); - status = pthread_create(&threads[0], NULL, read_write_thread, - (void *)&rcoll); - if (0 != status) - err_exit(status, "pthread_create"); - if (rcoll.debug) - fprintf(stderr, "Starting worker thread k=0\n"); - - /* wait for any broadcast */ - pthread_cleanup_push(cleanup_out, (void *)&rcoll); - status = - pthread_cond_wait(&rcoll.out_sync_cv, &rcoll.out_mutex); - if (0 != status) - err_exit(status, "cond out_sync_cv"); - pthread_cleanup_pop(0); - status = pthread_mutex_unlock(&rcoll.out_mutex); - if (0 != status) - err_exit(status, "unlock out_mutex"); - - /* now start the rest of the threads */ - for (k = 1; k < num_threads; ++k) { - status = - pthread_create(&threads[k], NULL, read_write_thread, - (void *)&rcoll); - if (0 != status) - err_exit(status, "pthread_create"); - if (rcoll.debug) - fprintf(stderr, "Starting worker thread k=%d\n", - k); - } - - /* now wait for worker threads to finish */ - for (k = 0; k < num_threads; ++k) { - status = pthread_join(threads[k], &vp); - if (0 != status) - err_exit(status, "pthread_join"); - if (rcoll.debug) - fprintf(stderr, - "Worker thread k=%d terminated\n", k); - } - } - - if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) { - struct timeval res_tm; - double a, b; - - gettimeofday(&end_tm, NULL); - res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec; - res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec; - if (res_tm.tv_usec < 0) { - --res_tm.tv_sec; - res_tm.tv_usec += 1000000; - } - a = res_tm.tv_sec; - a += (0.000001 * res_tm.tv_usec); - b = (double)rcoll.bs * (count - rcoll.out_done_count); - printf("time to transfer data was %d.%06d secs", - (int)res_tm.tv_sec, (int)res_tm.tv_usec); - if ((a > 0.00001) && (b > 511)) - printf(", %.2f MB/sec\n", b / (a * 1000000.0)); - else - printf("\n"); - } - if (do_sync) { - if (FT_SG == rcoll.out_type) { - fprintf(stderr, ">> Synchronizing cache on %s\n", outf); - res = sync_cache(rcoll.outfd); - if (2 == res) { - fprintf(stderr, - "Unit attention, media changed(in), continuing\n"); - res = sync_cache(rcoll.outfd); - } - if (0 != res) - fprintf(stderr, - "Unable to synchronize cache\n"); - } - } - - status = pthread_cancel(sig_listen_thread_id); - if (0 != status) - err_exit(status, "pthread_cancel"); - if (STDIN_FILENO != rcoll.infd) - close(rcoll.infd); - if ((STDOUT_FILENO != rcoll.outfd) && (FT_DEV_NULL != rcoll.out_type)) - close(rcoll.outfd); - res = 0; - if (0 != rcoll.out_count) { - fprintf(stderr, - ">>>> Some error occurred, remaining blocks=%d\n", - rcoll.out_count); - res = 2; - } - infull = count - rcoll.in_done_count - rcoll.in_partial; - fprintf(stderr, "%d+%d records in\n", infull, rcoll.in_partial); - outfull = count - rcoll.out_done_count - rcoll.out_partial; - fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial); - if (rcoll.dio_incomplete) { - int fd; - char c; - - fprintf(stderr, - ">> Direct IO requested but incomplete %d times\n", - rcoll.dio_incomplete); - if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { - if (1 == read(fd, &c, 1)) { - if ('0' == c) - fprintf(stderr, - ">>> %s set to '0' but should be set " - "to '1' for direct IO\n", - proc_allow_dio); - } - close(fd); - } - } - if (rcoll.sum_of_resids) - fprintf(stderr, ">> Non-zero sum of residual counts=%d\n", - rcoll.sum_of_resids); - return res; -} diff --git a/testcases/kernel/fs/scsi/ltpscsi/sg_err.c b/testcases/kernel/fs/scsi/ltpscsi/sg_err.c deleted file mode 100755 index 08eba2cf..00000000 --- a/testcases/kernel/fs/scsi/ltpscsi/sg_err.c +++ /dev/null @@ -1,1379 +0,0 @@ -#include -#include -#include -#include "sg_include.h" -#include "sg_err.h" - -/* This file is a huge cut, paste and hack from linux/drivers/scsi/constant.c -* which I guess was written by: -* Copyright (C) 1993, 1994, 1995 Eric Youngdale - -* The rest of this is: -* Copyright (C) 1999 - 2003 D. Gilbert -* -* 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, or (at your option) -* any later version. -* -* ASCII values for a number of symbolic constants, printing functions, etc. -* -* Some of the tables have been updated for SCSI 2. -* Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002) -* -* Version 0.89 (20030313) -* sense key specific field (bytes 15-17) decoding [Trent Piepho] -*/ - -#define OUTP stderr - -static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, - 16, 12, 10, 10 -}; - -#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7] - -static const char unknown[] = "UNKNOWN"; - -static const char *group_0_commands[] = { -/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense", -/* 04-07 */ "Format Unit", "Read Block Limits", unknown, - "Reasssign Blocks", -/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, - unknown, -/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", - "Inquiry", -/* 13-16 */ "Verify", "Recover Buffered Data", "Mode Select", "Reserve", -/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit", -/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", -/* 1e-1f */ "Prevent/Allow Medium Removal", unknown, -}; - -static const char *group_1_commands[] = { -/* 20-23 */ unknown, unknown, unknown, "Read Format capacities", -/* 24-28 */ "Set window", "Read Capacity", - unknown, unknown, "Read (10)", -/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase", - "Read updated block", -/* 2e-31 */ "Write Verify", "Verify", "Search High", "Search Equal", -/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", -/* 35-37 */ "Synchronize Cache", "Lock/Unlock Cache", - "Read Defect Data", -/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer", - "Read Buffer", -/* 3d-3f */ "Update Block", "Read Long", "Write Long", -}; - -static const char *group_2_commands[] = { -/* 40-41 */ "Change Definition", "Write Same", -/* 42-48 */ "Read sub-channel", "Read TOC", "Read header", - "Play audio (10)", "Get configuration", "Play audio msf", - "Play audio track/index", -/* 49-4f */ "Play track relative (10)", "Get event status notification", - "Pause/resume", "Log Select", "Log Sense", "Stop play/scan", - unknown, -/* 50-55 */ "Xdwrite", "Xpwrite, Read disk info", - "Xdread, Read track info", - "Reserve track", "Send OPC onfo", "Mode Select (10)", -/* 56-5b */ "Reserve (10)", "Release (10)", "Repair track", - "Read master cue", - "Mode Sense (10)", "Close track/session", -/* 5c-5f */ "Read buffer capacity", "Send cue sheet", - "Persistent reserve in", - "Persistent reserve out", -}; - -/* The following are 16 byte commands in group 4 */ -static const char *group_4_commands[] = { -/* 80-84 */ "Xdwrite (16)", "Rebuild (16)", "Regenerate (16)", - "Extended copy", - "Receive copy results", -/* 85-89 */ "Memory Export In (16)", "Access control in", - "Access control out", - "Read (16)", "Memory Export Out (16)", -/* 8a-8f */ "Write (16)", unknown, "Read attributes", - "Write attributes", - "Write and verify (16)", "Verify (16)", -/* 90-94 */ "Pre-fetch (16)", "Synchronize cache (16)", - "Lock/unlock cache (16)", "Write same (16)", unknown, -/* 95-99 */ unknown, unknown, unknown, unknown, unknown, -/* 9a-9f */ unknown, unknown, unknown, unknown, "Service action in", - "Service action out", -}; - -/* The following are 12 byte commands in group 5 */ -static const char *group_5_commands[] = { -/* a0-a5 */ "Report luns", "Blank", "Send event", "Maintenance (in)", - "Maintenance (out)", "Move medium/play audio(12)", -/* a6-a9 */ "Exchange medium", "Move medium attached", "Read(12)", - "Play track relative(12)", -/* aa-ae */ "Write(12)", unknown, "Erase(12), Get Performance", - "Read DVD structure", "Write and verify(12)", -/* af-b1 */ "Verify(12)", "Search data high(12)", - "Search data equal(12)", -/* b2-b4 */ "Search data low(12)", "Set limits(12)", - "Read element status attached", -/* b5-b6 */ "Request volume element address", - "Send volume tag, set streaming", -/* b7-b9 */ "Read defect data(12)", "Read element status", - "Read CD msf", -/* ba-bc */ "Redundancy group (in), Scan", - "Redundancy group (out), Set cd-rom speed", "Spare (in), Play cd", -/* bd-bf */ "Spare (out), Mechanism status", "Volume set (in), Read cd", - "Volume set (out), Send DVD structure", -}; - -#define group(opcode) (((opcode) >> 5) & 7) - -#define RESERVED_GROUP 0 -#define VENDOR_GROUP 1 - -static const char **commands[] = { - group_0_commands, group_1_commands, group_2_commands, - (const char **)RESERVED_GROUP, group_4_commands, - group_5_commands, (const char **)VENDOR_GROUP, - (const char **)VENDOR_GROUP -}; - -static const char reserved[] = "RESERVED"; -static const char vendor[] = "VENDOR SPECIFIC"; - -static void print_opcode(int opcode) -{ - const char **table = commands[group(opcode)]; - - switch ((unsigned long)table) { - case RESERVED_GROUP: - fprintf(OUTP, "%s(0x%02x)", reserved, opcode); - break; - case VENDOR_GROUP: - fprintf(OUTP, "%s(0x%02x)", vendor, opcode); - break; - default: - fprintf(OUTP, "%s", table[opcode & 0x1f]); - break; - } -} - -void sg_print_command(const unsigned char *command) -{ - int k, s; - print_opcode(command[0]); - fprintf(OUTP, " ["); - for (k = 0, s = COMMAND_SIZE(command[0]); k < s; ++k) - fprintf(OUTP, "%02x ", command[k]); - fprintf(OUTP, "]\n"); -} - -void sg_print_status(int masked_status) -{ - int scsi_status = (masked_status << 1) & 0x7e; - - sg_print_scsi_status(scsi_status); -} - -void sg_print_scsi_status(int scsi_status) -{ - const char *ccp; - - scsi_status &= 0x7e; /* sanitize as much as possible */ - switch (scsi_status) { - case 0: - ccp = "Good"; - break; - case 0x2: - ccp = "Check Condition"; - break; - case 0x4: - ccp = "Condition Met"; - break; - case 0x8: - ccp = "Busy"; - break; - case 0x10: - ccp = "Intermediate"; - break; - case 0x14: - ccp = "Intermediate-Condition Met"; - break; - case 0x18: - ccp = "Reservation Conflict"; - break; - case 0x22: - ccp = "Command Terminated (obsolete)"; - break; - case 0x28: - ccp = "Task set Full"; - break; - case 0x30: - ccp = "ACA Active"; - break; - case 0x40: - ccp = "Task Aborted"; - break; - default: - ccp = "Unknown status"; - break; - } - fprintf(OUTP, "%s ", ccp); -} - -/* In brackets is the related SCSI document (see www.t10.org) with the */ -/* peripheral device type after the colon */ -/* No programmatic use is made of these flags currently */ -#define D 0x0001 /* DIRECT ACCESS DEVICE (disk) [SBC-2: 0] */ -#define T 0x0002 /* SEQUENTIAL ACCESS DEVICE (tape) [SSC: 1] */ -#define L 0x0004 /* PRINTER DEVICE [SSC: 2] */ -#define P 0x0008 /* PROCESSOR DEVICE [SPC-2: 3] */ -#define W 0x0010 /* WRITE ONCE READ MULTIPLE DEVICE [SBC-2: 4] */ -#define R 0x0020 /* CD/DVD DEVICE [MMC-2: 5] */ -#define S 0x0040 /* SCANNER DEVICE [SCSI-2 (obsolete): 6] */ -#define O 0x0080 /* OPTICAL MEMORY DEVICE [SBC-2: 7] */ -#define M 0x0100 /* MEDIA CHANGER DEVICE [SMC-2: 8] */ -#define C 0x0200 /* COMMUNICATION DEVICE [SCSI-2 (obsolete): 9] */ -#define A 0x0400 /* ARRAY STORAGE [SCC-2: 12] */ -#define E 0x0800 /* ENCLOSURE SERVICES DEVICE [SES: 13] */ -#define B 0x1000 /* SIMPLIFIED DIRECT ACCESS DEVICE [RBC: 14] */ -#define K 0x2000 /* OPTICAL CARD READER/WRITER DEVICE [OCRW: 15] */ - -#define SC_ALL_DEVS ( D|T|L|P|W|R|S|O|M|C|A|E|B|K ) - -/* oft used strings are encoded using ASCII codes 0x1 to 0x1f . */ -/* This is to save space. This encoding should be UTF-8 and */ -/* UTF-16 friendly. */ -#define SC_AUDIO_PLAY_OPERATION "\x1" -#define SC_LOGICAL_UNIT "\x2" -#define SC_NOT_READY "\x3" -#define SC_OPERATION "\x4" -#define SC_IN_PROGRESS "\x5" -#define SC_HARDWARE_IF "\x6" -#define SC_CONTROLLER_IF "\x7" -#define SC_DATA_CHANNEL_IF "\x8" -#define SC_SERVO_IF "\x9" -#define SC_SPINDLE_IF "\xa" -#define SC_FIRMWARE_IF "\xb" -#define SC_RECOVERED_DATA "\xc" -#define SC_ERROR_RATE_TOO_HIGH "\xd" -#define SC_TIMES_TOO_HIGH "\xe" - -struct error_info { - unsigned char code1, code2; - unsigned short int devices; - const char *text; -}; - -struct error_info2 { - unsigned char code1, code2_min, code2_max; - unsigned short int devices; - const char *text; -}; - -static struct error_info2 additional2[] = { - {0x40, 0x00, 0x7f, D, "Ram failure (%x)"}, - {0x40, 0x80, 0xff, D | T | L | P | W | R | S | O | M | C, - "Diagnostic failure on component (%x)"}, - {0x41, 0x00, 0xff, D, "Data path failure (%x)"}, - {0x42, 0x00, 0xff, D, "Power-on or self-test failure (%x)"}, - {0, 0, 0, 0, NULL} -}; - -static struct error_info additional[] = { - {0x00, 0x00, SC_ALL_DEVS, "No additional sense information"}, - {0x00, 0x01, T, "Filemark detected"}, - {0x00, 0x02, T | S, "End-of-partition/medium detected"}, - {0x00, 0x03, T, "Setmark detected"}, - {0x00, 0x04, T | S, "Beginning-of-partition/medium detected"}, - {0x00, 0x05, T | L | S, "End-of-data detected"}, - {0x00, 0x06, SC_ALL_DEVS, "I/O process terminated"}, - {0x00, 0x11, R, SC_AUDIO_PLAY_OPERATION SC_IN_PROGRESS}, - {0x00, 0x12, R, SC_AUDIO_PLAY_OPERATION "paused"}, - {0x00, 0x13, R, SC_AUDIO_PLAY_OPERATION "successfully completed"}, - {0x00, 0x14, R, SC_AUDIO_PLAY_OPERATION "stopped due to error"}, - {0x00, 0x15, R, "No current audio status to return"}, - {0x00, 0x16, SC_ALL_DEVS, SC_OPERATION SC_IN_PROGRESS}, - {0x00, 0x17, D | T | L | W | R | S | O | M | A | E | B | K, - "Cleaning requested"}, - {0x00, 0x18, T, "Erase" SC_OPERATION SC_IN_PROGRESS}, - {0x00, 0x19, T, "Locate" SC_OPERATION SC_IN_PROGRESS}, - {0x00, 0x1a, T, "Rewind" SC_OPERATION SC_IN_PROGRESS}, - {0x00, 0x1b, T, "Set capacity" SC_OPERATION SC_IN_PROGRESS}, - {0x00, 0x1c, T, "Verify" SC_OPERATION SC_IN_PROGRESS}, - {0x01, 0x00, D | W | O | B | K, "No index/sector signal"}, - {0x02, 0x00, D | W | R | O | M | B | K, "No seek complete"}, - {0x03, 0x00, D | T | L | W | S | O | B | K, - "Peripheral device write fault"}, - {0x03, 0x01, T, "No write current"}, - {0x03, 0x02, T, "Excessive write errors"}, - {0x04, 0x00, SC_ALL_DEVS, - SC_LOGICAL_UNIT SC_NOT_READY "cause not reportable"}, - {0x04, 0x01, SC_ALL_DEVS, - SC_LOGICAL_UNIT "is" SC_IN_PROGRESS "of becoming ready"}, - {0x04, 0x02, SC_ALL_DEVS, - SC_LOGICAL_UNIT SC_NOT_READY "initializing cmd. required"}, - {0x04, 0x03, SC_ALL_DEVS, - SC_LOGICAL_UNIT SC_NOT_READY "manual intervention required"}, - {0x04, 0x04, D | T | L | R | O | B, - SC_LOGICAL_UNIT SC_NOT_READY "format" SC_IN_PROGRESS}, - {0x04, 0x05, D | T | W | O | M | C | A | B | K, - SC_LOGICAL_UNIT SC_NOT_READY "rebuild" SC_IN_PROGRESS}, - {0x04, 0x06, D | T | W | O | M | C | A | B | K, - SC_LOGICAL_UNIT SC_NOT_READY "recalculation" SC_IN_PROGRESS}, - {0x04, 0x07, SC_ALL_DEVS, - SC_LOGICAL_UNIT SC_NOT_READY SC_OPERATION SC_IN_PROGRESS}, - {0x04, 0x08, R, - SC_LOGICAL_UNIT SC_NOT_READY "long write" SC_IN_PROGRESS}, - {0x04, 0x09, SC_ALL_DEVS, - SC_LOGICAL_UNIT SC_NOT_READY "self-test" SC_IN_PROGRESS}, - {0x04, 0x0a, SC_ALL_DEVS, - SC_LOGICAL_UNIT "not accessible, asymmetric access state transition"}, - {0x04, 0x0b, SC_ALL_DEVS, - SC_LOGICAL_UNIT "not accessible, target port in standby state"}, - {0x04, 0x0c, SC_ALL_DEVS, - SC_LOGICAL_UNIT "not accessible, target port in unavailable state"}, - {0x04, 0x10, SC_ALL_DEVS, - SC_LOGICAL_UNIT SC_NOT_READY "auxiliary memory not accessible"}, - {0x05, 0x00, D | T | L | W | R | S | O | M | C | A | E | B | K, - SC_LOGICAL_UNIT "does not respond to selection"}, - {0x06, 0x00, D | W | R | O | M | B | K, "No reference position found"}, - {0x07, 0x00, D | T | L | W | R | S | O | M | B | K, - "Multiple peripheral devices selected"}, - {0x08, 0x00, D | T | L | W | R | S | O | M | C | A | E | B | K, - SC_LOGICAL_UNIT "communication failure"}, - {0x08, 0x01, D | T | L | W | R | S | O | M | C | A | E | B | K, - SC_LOGICAL_UNIT "communication time-out"}, - {0x08, 0x02, D | T | L | W | R | S | O | M | C | A | E | B | K, - SC_LOGICAL_UNIT "communication parity error"}, - {0x08, 0x03, D | T | R | O | M | B | K, - SC_LOGICAL_UNIT "communication CRC error (Ultra-DMA/32)"}, - {0x08, 0x04, D | T | L | P | W | R | S | O | C | K, - "Unreachable copy target"}, - {0x09, 0x00, D | T | W | R | O | B, "Track following error"}, - {0x09, 0x01, W | R | O | K, "Tracking servo failure"}, - {0x09, 0x02, W | R | O | K, "Focus servo failure"}, - {0x09, 0x03, W | R | O, "Spindle servo failure"}, - {0x09, 0x04, D | T | W | R | O | B, "Head select fault"}, - {0x0A, 0x00, SC_ALL_DEVS, "Error log overflow"}, - {0x0B, 0x00, SC_ALL_DEVS, "Warning"}, - {0x0B, 0x01, SC_ALL_DEVS, "Warning - specified temperature exceeded"}, - {0x0B, 0x02, SC_ALL_DEVS, "Warning - enclosure degraded"}, - {0x0C, 0x00, T | R | S, "Write error"}, - {0x0C, 0x01, K, "Write error - recovered with auto reallocation"}, - {0x0C, 0x02, D | W | O | B | K, - "Write error - auto reallocation failed"}, - {0x0C, 0x03, D | W | O | B | K, "Write error - recommend reassignment"}, - {0x0C, 0x04, D | T | W | O | B, "Compression check miscompare error"}, - {0x0C, 0x05, D | T | W | O | B, - "Data expansion occurred during compression"}, - {0x0C, 0x06, D | T | W | O | B, "Block not compressible"}, - {0x0C, 0x07, R, "Write error - recovery needed"}, - {0x0C, 0x08, R, "Write error - recovery failed"}, - {0x0C, 0x09, R, "Write error - loss of streaming"}, - {0x0C, 0x0A, R, "Write error - padding blocks added"}, - {0x0C, 0x0B, D | T | W | R | O | M | B, "Auxiliary memory write error"}, - {0x0C, 0x0C, SC_ALL_DEVS, "Write error - unexpected unsolicited data"}, - {0x0C, 0x0D, SC_ALL_DEVS, "Write error - not enough unsolicited data"}, - {0x0D, 0x00, D | T | L | P | W | R | S | O | C | A | K, - "Error detected by third party temporary initiator"}, - {0x0D, 0x01, D | T | L | P | W | R | S | O | C | A | K, - "Third party device failure"}, - {0x0D, 0x02, D | T | L | P | W | R | S | O | C | A | K, - "Copy target device not reachable"}, - {0x0D, 0x03, D | T | L | P | W | R | S | O | C | A | K, - "Incorrect copy target device"}, - {0x0D, 0x04, D | T | L | P | W | R | S | O | C | A | K, - "Copy target device underrun"}, - {0x0D, 0x05, D | T | L | P | W | R | S | O | C | A | K, - "Copy target device overrun"}, - {0x10, 0x00, D | W | O | B | K, "Id CRC or ECC error"}, - {0x11, 0x00, D | T | W | R | S | O | B | K, "Unrecovered read error"}, - {0x11, 0x01, D | T | W | R | S | O | B | K, "Read retries exhausted"}, - {0x11, 0x02, D | T | W | R | S | O | B | K, - "Error too long to correct"}, - {0x11, 0x03, D | T | W | S | O | B | K, "Multiple read errors"}, - {0x11, 0x04, D | W | O | B | K, - "Unrecovered read error - auto reallocate failed"}, - {0x11, 0x05, W | R | O | B, "L-EC uncorrectable error"}, - {0x11, 0x06, W | R | O | B, "CIRC unrecovered error"}, - {0x11, 0x07, W | O | B, "Data re-synchronization error"}, - {0x11, 0x08, T, "Incomplete block read"}, - {0x11, 0x09, T, "No gap found"}, - {0x11, 0x0A, D | T | O | B | K, "Miscorrected error"}, - {0x11, 0x0B, D | W | O | B | K, - "Unrecovered read error - recommend reassignment"}, - {0x11, 0x0C, D | W | O | B | K, - "Unrecovered read error - recommend rewrite the data"}, - {0x11, 0x0D, D | T | W | R | O | B, "De-compression CRC error"}, - {0x11, 0x0E, D | T | W | R | O | B, - "Cannot decompress using declared algorithm"}, - {0x11, 0x0F, R, "Error reading UPC/EAN number"}, - {0x11, 0x10, R, "Error reading ISRC number"}, - {0x11, 0x11, R, "Read error - loss of streaming"}, - {0x11, 0x12, D | T | W | R | O | M | B, "Auxiliary memory read error"}, - {0x11, 0x13, SC_ALL_DEVS, "Read error - failed retransmission request"}, - {0x12, 0x00, D | W | O | B | K, "Address mark not found for id field"}, - {0x13, 0x00, D | W | O | B | K, - "Address mark not found for data field"}, - {0x14, 0x00, D | T | L | W | R | S | O | B | K, - "Recorded entity not found"}, - {0x14, 0x01, D | T | W | R | O | B | K, "Record not found"}, - {0x14, 0x02, T, "Filemark or setmark not found"}, - {0x14, 0x03, T, "End-of-data not found"}, - {0x14, 0x04, T, "Block sequence error"}, - {0x14, 0x05, D | T | W | O | B | K, - "Record not found - recommend reassignment"}, - {0x14, 0x06, D | T | W | O | B | K, - "Record not found - data auto-reallocated"}, - {0x14, 0x07, T, "Locate" SC_OPERATION " failure"}, - {0x15, 0x00, D | T | L | W | R | S | O | M | B | K, - "Random positioning error"}, - {0x15, 0x01, D | T | L | W | R | S | O | M | B | K, - "Mechanical positioning error"}, - {0x15, 0x02, D | T | W | R | O | B | K, - "Positioning error detected by read of medium"}, - {0x16, 0x00, D | W | O | B | K, "Data synchronization mark error"}, - {0x16, 0x01, D | W | O | B | K, "Data sync error - data rewritten"}, - {0x16, 0x02, D | W | O | B | K, "Data sync error - recommend rewrite"}, - {0x16, 0x03, D | W | O | B | K, - "Data sync error - data auto-reallocated"}, - {0x16, 0x04, D | W | O | B | K, - "Data sync error - recommend reassignment"}, - {0x17, 0x00, D | T | W | R | S | O | B | K, - SC_RECOVERED_DATA "with no error correction applied"}, - {0x17, 0x01, D | T | W | R | S | O | B | K, - SC_RECOVERED_DATA "with retries"}, - {0x17, 0x02, D | T | W | R | O | B | K, - SC_RECOVERED_DATA "with positive head offset"}, - {0x17, 0x03, D | T | W | R | O | B | K, - SC_RECOVERED_DATA "with negative head offset"}, - {0x17, 0x04, W | R | O | B, - SC_RECOVERED_DATA "with retries and/or circ applied"}, - {0x17, 0x05, D | W | R | O | B | K, - SC_RECOVERED_DATA "using previous sector id"}, - {0x17, 0x06, D | W | O | B | K, - SC_RECOVERED_DATA "without ecc - data auto-reallocated"}, - {0x17, 0x07, D | W | R | O | B | K, - SC_RECOVERED_DATA "without ecc - recommend reassignment"}, - {0x17, 0x08, D | W | R | O | B | K, - SC_RECOVERED_DATA "without ecc - recommend rewrite"}, - {0x17, 0x09, D | W | R | O | B | K, - SC_RECOVERED_DATA "without ecc - data rewritten"}, - {0x18, 0x00, D | T | W | R | O | B | K, - SC_RECOVERED_DATA "with error correction applied"}, - {0x18, 0x01, D | W | R | O | B | K, - SC_RECOVERED_DATA "with error corr. & retries applied"}, - {0x18, 0x02, D | W | R | O | B | K, - SC_RECOVERED_DATA "- data auto-reallocated"}, - {0x18, 0x03, R, SC_RECOVERED_DATA "with CIRC"}, - {0x18, 0x04, R, SC_RECOVERED_DATA "with L-EC"}, - {0x18, 0x05, D | W | R | O | B | K, - SC_RECOVERED_DATA "- recommend reassignment"}, - {0x18, 0x06, D | W | R | O | B | K, - SC_RECOVERED_DATA "- recommend rewrite"}, - {0x18, 0x07, D | W | O | B | K, - SC_RECOVERED_DATA "with ecc - data rewritten"}, - {0x18, 0x08, R, SC_RECOVERED_DATA "with linking"}, - {0x19, 0x00, D | O | K, "Defect list error"}, - {0x19, 0x01, D | O | K, "Defect list not available"}, - {0x19, 0x02, D | O | K, "Defect list error in primary list"}, - {0x19, 0x03, D | O | K, "Defect list error in grown list"}, - {0x1A, 0x00, SC_ALL_DEVS, "Parameter list length error"}, - {0x1B, 0x00, SC_ALL_DEVS, "Synchronous data transfer error"}, - {0x1C, 0x00, D | O | B | K, "Defect list not found"}, - {0x1C, 0x01, D | O | B | K, "Primary defect list not found"}, - {0x1C, 0x02, D | O | B | K, "Grown defect list not found"}, - {0x1D, 0x00, D | T | W | R | O | B | K, - "Miscompare during verify" SC_OPERATION}, - {0x1E, 0x00, D | W | O | B | K, "Recovered id with ecc correction"}, - {0x1F, 0x00, D | O | K, "Partial defect list transfer"}, - {0x20, 0x00, SC_ALL_DEVS, "Invalid command" SC_OPERATION " code"}, - {0x20, 0x01, D | T | P | W | R | O | M | A | E | B | K, - "Access denied - initiator pending-enrolled"}, - {0x20, 0x02, D | T | P | W | R | O | M | A | E | B | K, - "Access denied - no access rights"}, - {0x20, 0x03, D | T | P | W | R | O | M | A | E | B | K, - "Access denied - no mgmt id key"}, - {0x20, 0x04, T, "Illegal command while in write capable state"}, - {0x20, 0x05, T, "Obsolete"}, - {0x20, 0x06, T, "Illegal command while in explicit address mode"}, - {0x20, 0x07, T, "Illegal command while in implicit address mode"}, - {0x20, 0x08, D | T | P | W | R | O | M | A | E | B | K, - "Access denied - enrollment conflict"}, - {0x20, 0x09, D | T | P | W | R | O | M | A | E | B | K, - "Access denied - invalid LU identifier"}, - {0x20, 0x0A, D | T | P | W | R | O | M | A | E | B | K, - "Access denied - invalid proxy token"}, - {0x20, 0x0B, D | T | P | W | R | O | M | A | E | B | K, - "Access denied - ACL LUN conflict"}, - {0x21, 0x00, D | T | W | R | O | M | B | K, - "Logical block address out of range"}, - {0x21, 0x01, D | T | W | R | O | M | B | K, "Invalid element address"}, - {0x21, 0x02, R, "Invalid address for write"}, - {0x22, 0x00, D, "Illegal function (use 20 00,24 00,or 26 00)"}, - {0x24, 0x00, SC_ALL_DEVS, "Invalid field in cdb"}, - {0x24, 0x01, SC_ALL_DEVS, "CDB decryption error"}, - {0x25, 0x00, SC_ALL_DEVS, SC_LOGICAL_UNIT "not supported"}, - {0x26, 0x00, SC_ALL_DEVS, "Invalid field in parameter list"}, - {0x26, 0x01, SC_ALL_DEVS, "Parameter not supported"}, - {0x26, 0x02, SC_ALL_DEVS, "Parameter value invalid"}, - {0x26, 0x03, D | T | L | P | W | R | S | O | M | C | A | E | K, - "Threshold parameters not supported"}, - {0x26, 0x04, SC_ALL_DEVS, "Invalid release of persistent reservation"}, - {0x26, 0x05, D | T | L | P | W | R | S | O | M | C | A | B | K, - "Data decryption error"}, - {0x26, 0x06, D | T | L | P | W | R | S | O | C | K, - "Too many target descriptors"}, - {0x26, 0x07, D | T | L | P | W | R | S | O | C | K, - "Unsupported target descriptor type code"}, - {0x26, 0x08, D | T | L | P | W | R | S | O | C | K, - "Too many segment descriptors"}, - {0x26, 0x09, D | T | L | P | W | R | S | O | C | K, - "Unsupported segment descriptor type code"}, - {0x26, 0x0A, D | T | L | P | W | R | S | O | C | K, - "Unexpected inexact segment"}, - {0x26, 0x0B, D | T | L | P | W | R | S | O | C | K, - "Inline data length exceeded"}, - {0x26, 0x0C, D | T | L | P | W | R | S | O | C | K, - "Invalid" SC_OPERATION " for copy source or destination"}, - {0x26, 0x0D, D | T | L | P | W | R | S | O | C | K, - "Copy segment granularity violation"}, - {0x27, 0x00, D | T | W | R | O | B | K, "Write protected"}, - {0x27, 0x01, D | T | W | R | O | B | K, "Hardware write protected"}, - {0x27, 0x02, D | T | W | R | O | B | K, - SC_LOGICAL_UNIT "software write protected"}, - {0x27, 0x03, T | R, "Associated write protect"}, - {0x27, 0x04, T | R, "Persistent write protect"}, - {0x27, 0x05, T | R, "Permanent write protect"}, - {0x27, 0x06, R, "Conditional write protect"}, - {0x28, 0x00, SC_ALL_DEVS, - "Not ready to ready change, medium may have changed"}, - {0x28, 0x01, D | T | W | R | O | M | B, - "Import or export element accessed"}, - {0x29, 0x00, SC_ALL_DEVS, - "Power on,reset,or bus device reset occurred"}, - {0x29, 0x01, SC_ALL_DEVS, "Power on occurred"}, - {0x29, 0x02, SC_ALL_DEVS, "Scsi bus reset occurred"}, - {0x29, 0x03, SC_ALL_DEVS, "Bus device reset function occurred"}, - {0x29, 0x04, SC_ALL_DEVS, "Device internal reset"}, - {0x29, 0x05, SC_ALL_DEVS, "Transceiver mode changed to single-ended"}, - {0x29, 0x06, SC_ALL_DEVS, "Transceiver mode changed to lvd"}, - {0x29, 0x07, SC_ALL_DEVS, "I_T nexus loss occurred"}, - {0x2A, 0x00, D | T | L | W | R | S | O | M | C | A | E | B | K, - "Parameters changed"}, - {0x2A, 0x01, D | T | L | W | R | S | O | M | C | A | E | B | K, - "Mode parameters changed"}, - {0x2A, 0x02, D | T | L | W | R | S | O | M | C | A | E | K, - "Log parameters changed"}, - {0x2A, 0x03, D | T | L | P | W | R | S | O | M | C | A | E | K, - "Reservations preempted"}, - {0x2A, 0x04, D | T | L | P | W | R | S | O | M | C | A | E, - "Reservations released"}, - {0x2A, 0x05, D | T | L | P | W | R | S | O | M | C | A | E, - "Registrations preempted"}, - {0x2A, 0x06, SC_ALL_DEVS, "Asymmetric access state changed"}, - {0x2A, 0x07, SC_ALL_DEVS, - "Implicit asymmetric access state transition failed"}, - {0x2B, 0x00, D | T | L | P | W | R | S | O | C | K, - "Copy cannot execute since host cannot disconnect"}, - {0x2C, 0x00, SC_ALL_DEVS, "Command sequence error"}, - {0x2C, 0x01, S, "Too many windows specified"}, - {0x2C, 0x02, S, "Invalid combination of windows specified"}, - {0x2C, 0x03, R, "Current program area is not empty"}, - {0x2C, 0x04, R, "Current program area is empty"}, - {0x2C, 0x05, B, "Illegal power condition request"}, - {0x2C, 0x06, R, "Persistent prevent conflict"}, - {0x2C, 0x07, SC_ALL_DEVS, "Previous busy status"}, - {0x2C, 0x08, SC_ALL_DEVS, "Previous task set full status"}, - {0x2C, 0x09, D | T | L | P | W | R | S | O | M | E | B | K, - "Previous reservation conflict status"}, - {0x2D, 0x00, T, "Overwrite error on update in place"}, - {0x2F, 0x00, SC_ALL_DEVS, "Commands cleared by another initiator"}, - {0x30, 0x00, D | T | W | R | O | M | B | K, - "Incompatible medium installed"}, - {0x30, 0x01, D | T | W | R | O | B | K, - "Cannot read medium - unknown format"}, - {0x30, 0x02, D | T | W | R | O | B | K, - "Cannot read medium - incompatible format"}, - {0x30, 0x03, D | T | R | K, "Cleaning cartridge installed"}, - {0x30, 0x04, D | T | W | R | O | B | K, - "Cannot write medium - unknown format"}, - {0x30, 0x05, D | T | W | R | O | B | K, - "Cannot write medium - incompatible format"}, - {0x30, 0x06, D | T | W | R | O | B, - "Cannot format medium - incompatible medium"}, - {0x30, 0x07, D | T | L | W | R | S | O | M | A | E | B | K, - "Cleaning failure"}, - {0x30, 0x08, R, "Cannot write - application code mismatch"}, - {0x30, 0x09, R, "Current session not fixated for append"}, - {0x30, 0x10, R, "Medium not formatted"}, /* should ascq be 0xa ?? */ - {0x31, 0x00, D | T | W | R | O | B | K, "Medium format corrupted"}, - {0x31, 0x01, D | L | R | O | B, "Format command failed"}, - {0x31, 0x02, R, "Zoned formatting failed due to spare linking"}, - {0x32, 0x00, D | W | O | B | K, "No defect spare location available"}, - {0x32, 0x01, D | W | O | B | K, "Defect list update failure"}, - {0x33, 0x00, T, "Tape length error"}, - {0x34, 0x00, SC_ALL_DEVS, "Enclosure failure"}, - {0x35, 0x00, SC_ALL_DEVS, "Enclosure services failure"}, - {0x35, 0x01, SC_ALL_DEVS, "Unsupported enclosure function"}, - {0x35, 0x02, SC_ALL_DEVS, "Enclosure services unavailable"}, - {0x35, 0x03, SC_ALL_DEVS, "Enclosure services transfer failure"}, - {0x35, 0x04, SC_ALL_DEVS, "Enclosure services transfer refused"}, - {0x36, 0x00, L, "Ribbon,ink,or toner failure"}, - {0x37, 0x00, D | T | L | W | R | S | O | M | C | A | E | B | K, - "Rounded parameter"}, - {0x38, 0x00, B, "Event status notification"}, - {0x38, 0x02, B, "Esn - power management class event"}, - {0x38, 0x04, B, "Esn - media class event"}, - {0x38, 0x06, B, "Esn - device busy class event"}, - {0x39, 0x00, D | T | L | W | R | S | O | M | C | A | E | K, - "Saving parameters not supported"}, - {0x3A, 0x00, D | T | L | W | R | S | O | M | B | K, - "Medium not present"}, - {0x3A, 0x01, D | T | W | R | O | M | B | K, - "Medium not present - tray closed"}, - {0x3A, 0x02, D | T | W | R | O | M | B | K, - "Medium not present - tray open"}, - {0x3A, 0x03, D | T | W | R | O | M | B, - "Medium not present - loadable"}, - {0x3A, 0x04, D | T | W | R | O | M | B, - "Medium not present - medium auxiliary memory accessible"}, - {0x3B, 0x00, T | L, "Sequential positioning error"}, - {0x3B, 0x01, T, "Tape position error at beginning-of-medium"}, - {0x3B, 0x02, T, "Tape position error at end-of-medium"}, - {0x3B, 0x03, L, "Tape or electronic vertical forms unit " SC_NOT_READY}, - {0x3B, 0x04, L, "Slew failure"}, - {0x3B, 0x05, L, "Paper jam"}, - {0x3B, 0x06, L, "Failed to sense top-of-form"}, - {0x3B, 0x07, L, "Failed to sense bottom-of-form"}, - {0x3B, 0x08, T, "Reposition error"}, - {0x3B, 0x09, S, "Read past end of medium"}, - {0x3B, 0x0A, S, "Read past beginning of medium"}, - {0x3B, 0x0B, S, "Position past end of medium"}, - {0x3B, 0x0C, T | S, "Position past beginning of medium"}, - {0x3B, 0x0D, D | T | W | R | O | M | B | K, - "Medium destination element full"}, - {0x3B, 0x0E, D | T | W | R | O | M | B | K, - "Medium source element empty"}, - {0x3B, 0x0F, R, "End of medium reached"}, - {0x3B, 0x11, D | T | W | R | O | M | B | K, - "Medium magazine not accessible"}, - {0x3B, 0x12, D | T | W | R | O | M | B | K, "Medium magazine removed"}, - {0x3B, 0x13, D | T | W | R | O | M | B | K, "Medium magazine inserted"}, - {0x3B, 0x14, D | T | W | R | O | M | B | K, "Medium magazine locked"}, - {0x3B, 0x15, D | T | W | R | O | M | B | K, "Medium magazine unlocked"}, - {0x3B, 0x16, R, "Mechanical positioning or changer error"}, - {0x3D, 0x00, D | T | L | P | W | R | S | O | M | C | A | E | K, - "Invalid bits in identify message"}, - {0x3E, 0x00, SC_ALL_DEVS, - SC_LOGICAL_UNIT "has not self-configured yet"}, - {0x3E, 0x01, SC_ALL_DEVS, SC_LOGICAL_UNIT "failure"}, - {0x3E, 0x02, SC_ALL_DEVS, "Timeout on logical unit"}, - {0x3E, 0x03, SC_ALL_DEVS, SC_LOGICAL_UNIT "failed self-test"}, - {0x3E, 0x04, SC_ALL_DEVS, - SC_LOGICAL_UNIT "unable to update self-test log"}, - {0x3F, 0x00, SC_ALL_DEVS, "Target operating conditions have changed"}, - {0x3F, 0x01, SC_ALL_DEVS, "Microcode has been changed"}, - {0x3F, 0x02, D | T | L | P | W | R | S | O | M | C | B | K, - "Changed operating definition"}, - {0x3F, 0x03, SC_ALL_DEVS, "Inquiry data has changed"}, - {0x3F, 0x04, D | T | W | R | O | M | C | A | E | B | K, - "Component device attached"}, - {0x3F, 0x05, D | T | W | R | O | M | C | A | E | B | K, - "Device identifier changed"}, - {0x3F, 0x06, D | T | W | R | O | M | C | A | E | B, - "Redundancy group created or modified"}, - {0x3F, 0x07, D | T | W | R | O | M | C | A | E | B, - "Redundancy group deleted"}, - {0x3F, 0x08, D | T | W | R | O | M | C | A | E | B, - "Spare created or modified"}, - {0x3F, 0x09, D | T | W | R | O | M | C | A | E | B, "Spare deleted"}, - {0x3F, 0x0A, D | T | W | R | O | M | C | A | E | B | K, - "Volume set created or modified"}, - {0x3F, 0x0B, D | T | W | R | O | M | C | A | E | B | K, - "Volume set deleted"}, - {0x3F, 0x0C, D | T | W | R | O | M | C | A | E | B | K, - "Volume set deassigned"}, - {0x3F, 0x0D, D | T | W | R | O | M | C | A | E | B | K, - "Volume set reassigned"}, - {0x3F, 0x0E, D | T | L | P | W | R | S | O | M | C | A | E, - "Reported luns data has changed"}, - {0x3F, 0x10, D | T | W | R | O | M | B, "Medium loadable"}, - {0x3F, 0x11, D | T | W | R | O | M | B, - "Medium auxiliary memory accessible"}, - {0x40, 0x00, D, "Ram failure (should use 40 nn)"}, - /* - * FIXME(eric) - need a way to represent wildcards here. - */ - {0x40, 0x00, SC_ALL_DEVS, - "Diagnostic failure on component nn (80h-ffh)"}, - {0x41, 0x00, D, "Data path failure (should use 40 nn)"}, - {0x42, 0x00, D, "Power-on or self-test failure (should use 40 nn)"}, - {0x43, 0x00, SC_ALL_DEVS, "Message error"}, - {0x44, 0x00, SC_ALL_DEVS, "Internal target failure"}, - {0x45, 0x00, SC_ALL_DEVS, "Select or reselect failure"}, - {0x46, 0x00, D | T | L | P | W | R | S | O | M | C | B | K, - "Unsuccessful soft reset"}, - {0x47, 0x00, SC_ALL_DEVS, "Scsi parity error"}, - {0x47, 0x01, SC_ALL_DEVS, "Data phase CRC error detected"}, - {0x47, 0x02, SC_ALL_DEVS, - "Scsi parity error detected during st data phase"}, - {0x47, 0x03, SC_ALL_DEVS, "Information unit CRC error detected"}, - {0x47, 0x04, SC_ALL_DEVS, - "Asynchronous information protection error detected"}, - {0x47, 0x05, SC_ALL_DEVS, "Protocol service CRC error"}, - {0x48, 0x00, SC_ALL_DEVS, "Initiator detected error message received"}, - {0x49, 0x00, SC_ALL_DEVS, "Invalid message error"}, - {0x4A, 0x00, SC_ALL_DEVS, "Command phase error"}, - {0x4B, 0x00, SC_ALL_DEVS, "Data phase error"}, - {0x4C, 0x00, SC_ALL_DEVS, SC_LOGICAL_UNIT "failed self-configuration"}, - /* - * FIXME(eric) - need a way to represent wildcards here. - */ - {0x4D, 0x00, SC_ALL_DEVS, - "Tagged overlapped commands (nn = queue tag)"}, - {0x4E, 0x00, SC_ALL_DEVS, "Overlapped commands attempted"}, - {0x50, 0x00, T, "Write append error"}, - {0x50, 0x01, T, "Write append position error"}, - {0x50, 0x02, T, "Position error related to timing"}, - {0x51, 0x00, T | R | O, "Erase failure"}, - {0x52, 0x00, T, "Cartridge fault"}, - {0x53, 0x00, D | T | L | W | R | S | O | M | B | K, - "Media load or eject failed"}, - {0x53, 0x01, T, "Unload tape failure"}, - {0x53, 0x02, D | T | W | R | O | M | B | K, "Medium removal prevented"}, - {0x54, 0x00, P, "Scsi to host system interface failure"}, - {0x55, 0x00, P, "System resource failure"}, - {0x55, 0x01, D | O | B | K, "System buffer full"}, - {0x55, 0x02, D | T | L | P | W | R | S | O | M | A | E | K, - "Insufficient reservation resources"}, - {0x55, 0x03, D | T | L | P | W | R | S | O | M | C | A | E, - "Insufficient resources"}, - {0x55, 0x04, D | T | L | P | W | R | S | O | M | A | E, - "Insufficient registration resources"}, - {0x55, 0x05, D | T | P | W | R | O | M | A | E | B | K, - "Insufficient access control resources"}, - {0x55, 0x06, D | T | W | R | O | M | B, - "Auxiliary memory out of space"}, - {0x57, 0x00, R, "Unable to recover table-of-contents"}, - {0x58, 0x00, O, "Generation does not exist"}, - {0x59, 0x00, O, "Updated block read"}, - {0x5A, 0x00, D | T | L | P | W | R | S | O | M | B | K, - "Operator request or state change input"}, - {0x5A, 0x01, D | T | W | R | O | M | B | K, - "Operator medium removal request"}, - {0x5A, 0x02, D | T | W | R | O | A | B | K, - "Operator selected write protect"}, - {0x5A, 0x03, D | T | W | R | O | A | B | K, - "Operator selected write permit"}, - {0x5B, 0x00, D | T | L | P | W | R | S | O | M | K, "Log exception"}, - {0x5B, 0x01, D | T | L | P | W | R | S | O | M | K, - "Threshold condition met"}, - {0x5B, 0x02, D | T | L | P | W | R | S | O | M | K, - "Log counter at maximum"}, - {0x5B, 0x03, D | T | L | P | W | R | S | O | M | K, - "Log list codes exhausted"}, - {0x5C, 0x00, D | O, "Rpl status change"}, - {0x5C, 0x01, D | O, "Spindles synchronized"}, - {0x5C, 0x02, D | O, "Spindles not synchronized"}, - {0x5D, 0x00, SC_ALL_DEVS, "Failure prediction threshold exceeded"}, - {0x5D, 0x01, R | B, "Media failure prediction threshold exceeded"}, - {0x5D, 0x02, R, - SC_LOGICAL_UNIT "failure prediction threshold exceeded"}, - {0x5D, 0x03, R, "spare area exhaustion prediction threshold exceeded"}, - /* large series of "impending failure" messages */ - {0x5D, 0x10, D | B, SC_HARDWARE_IF "general hard drive failure"}, - {0x5D, 0x11, D | B, SC_HARDWARE_IF "drive" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x12, D | B, SC_HARDWARE_IF "data" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x13, D | B, SC_HARDWARE_IF "seek" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x14, D | B, SC_HARDWARE_IF "too many block reassigns"}, - {0x5D, 0x15, D | B, SC_HARDWARE_IF "access" SC_TIMES_TOO_HIGH}, - {0x5D, 0x16, D | B, SC_HARDWARE_IF "start unit" SC_TIMES_TOO_HIGH}, - {0x5D, 0x17, D | B, SC_HARDWARE_IF "channel parametrics"}, - {0x5D, 0x18, D | B, SC_HARDWARE_IF "controller detected"}, - {0x5D, 0x19, D | B, SC_HARDWARE_IF "throughput performance"}, - {0x5D, 0x1A, D | B, SC_HARDWARE_IF "seek time performance"}, - {0x5D, 0x1B, D | B, SC_HARDWARE_IF "spin-up retry count"}, - {0x5D, 0x1C, D | B, SC_HARDWARE_IF "drive calibration retry count"}, - {0x5D, 0x20, D | B, SC_CONTROLLER_IF "general hard drive failure"}, - {0x5D, 0x21, D | B, SC_CONTROLLER_IF "drive" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x22, D | B, SC_CONTROLLER_IF "data" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x23, D | B, SC_CONTROLLER_IF "seek" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x24, D | B, SC_CONTROLLER_IF "too many block reassigns"}, - {0x5D, 0x25, D | B, SC_CONTROLLER_IF "access" SC_TIMES_TOO_HIGH}, - {0x5D, 0x26, D | B, SC_CONTROLLER_IF "start unit" SC_TIMES_TOO_HIGH}, - {0x5D, 0x27, D | B, SC_CONTROLLER_IF "channel parametrics"}, - {0x5D, 0x28, D | B, SC_CONTROLLER_IF "controller detected"}, - {0x5D, 0x29, D | B, SC_CONTROLLER_IF "throughput performance"}, - {0x5D, 0x2A, D | B, SC_CONTROLLER_IF "seek time performance"}, - {0x5D, 0x2B, D | B, SC_CONTROLLER_IF "spin-up retry count"}, - {0x5D, 0x2C, D | B, SC_CONTROLLER_IF "drive calibration retry count"}, - {0x5D, 0x30, D | B, SC_DATA_CHANNEL_IF "general hard drive failure"}, - {0x5D, 0x31, D | B, SC_DATA_CHANNEL_IF "drive" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x32, D | B, SC_DATA_CHANNEL_IF "data" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x33, D | B, SC_DATA_CHANNEL_IF "seek" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x34, D | B, SC_DATA_CHANNEL_IF "too many block reassigns"}, - {0x5D, 0x35, D | B, SC_DATA_CHANNEL_IF "access" SC_TIMES_TOO_HIGH}, - {0x5D, 0x36, D | B, SC_DATA_CHANNEL_IF "start unit" SC_TIMES_TOO_HIGH}, - {0x5D, 0x37, D | B, SC_DATA_CHANNEL_IF "channel parametrics"}, - {0x5D, 0x38, D | B, SC_DATA_CHANNEL_IF "controller detected"}, - {0x5D, 0x39, D | B, SC_DATA_CHANNEL_IF "throughput performance"}, - {0x5D, 0x3A, D | B, SC_DATA_CHANNEL_IF "seek time performance"}, - {0x5D, 0x3B, D | B, SC_DATA_CHANNEL_IF "spin-up retry count"}, - {0x5D, 0x3C, D | B, SC_DATA_CHANNEL_IF "drive calibration retry count"}, - {0x5D, 0x40, D | B, SC_SERVO_IF "general hard drive failure"}, - {0x5D, 0x41, D | B, SC_SERVO_IF "drive" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x42, D | B, SC_SERVO_IF "data" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x43, D | B, SC_SERVO_IF "seek" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x44, D | B, SC_SERVO_IF "too many block reassigns"}, - {0x5D, 0x45, D | B, SC_SERVO_IF "access" SC_TIMES_TOO_HIGH}, - {0x5D, 0x46, D | B, SC_SERVO_IF "start unit" SC_TIMES_TOO_HIGH}, - {0x5D, 0x47, D | B, SC_SERVO_IF "channel parametrics"}, - {0x5D, 0x48, D | B, SC_SERVO_IF "controller detected"}, - {0x5D, 0x49, D | B, SC_SERVO_IF "throughput performance"}, - {0x5D, 0x4A, D | B, SC_SERVO_IF "seek time performance"}, - {0x5D, 0x4B, D | B, SC_SERVO_IF "spin-up retry count"}, - {0x5D, 0x4C, D | B, SC_SERVO_IF "drive calibration retry count"}, - {0x5D, 0x50, D | B, SC_SPINDLE_IF "general hard drive failure"}, - {0x5D, 0x51, D | B, SC_SPINDLE_IF "drive" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x52, D | B, SC_SPINDLE_IF "data" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x53, D | B, SC_SPINDLE_IF "seek" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x54, D | B, SC_SPINDLE_IF "too many block reassigns"}, - {0x5D, 0x55, D | B, SC_SPINDLE_IF "access" SC_TIMES_TOO_HIGH}, - {0x5D, 0x56, D | B, SC_SPINDLE_IF "start unit" SC_TIMES_TOO_HIGH}, - {0x5D, 0x57, D | B, SC_SPINDLE_IF "channel parametrics"}, - {0x5D, 0x58, D | B, SC_SPINDLE_IF "controller detected"}, - {0x5D, 0x59, D | B, SC_SPINDLE_IF "throughput performance"}, - {0x5D, 0x5A, D | B, SC_SPINDLE_IF "seek time performance"}, - {0x5D, 0x5B, D | B, SC_SPINDLE_IF "spin-up retry count"}, - {0x5D, 0x5C, D | B, SC_SPINDLE_IF "drive calibration retry count"}, - {0x5D, 0x60, D | B, SC_FIRMWARE_IF "general hard drive failure"}, - {0x5D, 0x61, D | B, SC_FIRMWARE_IF "drive" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x62, D | B, SC_FIRMWARE_IF "data" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x63, D | B, SC_FIRMWARE_IF "seek" SC_ERROR_RATE_TOO_HIGH}, - {0x5D, 0x64, D | B, SC_FIRMWARE_IF "too many block reassigns"}, - {0x5D, 0x65, D | B, SC_FIRMWARE_IF "access" SC_TIMES_TOO_HIGH}, - {0x5D, 0x66, D | B, SC_FIRMWARE_IF "start unit" SC_TIMES_TOO_HIGH}, - {0x5D, 0x67, D | B, SC_FIRMWARE_IF "channel parametrics"}, - {0x5D, 0x68, D | B, SC_FIRMWARE_IF "controller detected"}, - {0x5D, 0x69, D | B, SC_FIRMWARE_IF "throughput performance"}, - {0x5D, 0x6A, D | B, SC_FIRMWARE_IF "seek time performance"}, - {0x5D, 0x6B, D | B, SC_FIRMWARE_IF "spin-up retry count"}, - {0x5D, 0x6C, D | B, SC_FIRMWARE_IF "drive calibration retry count"}, - {0x5D, 0xFF, SC_ALL_DEVS, - "Failure prediction threshold exceeded (false)"}, - {0x5E, 0x00, D | T | L | P | W | R | S | O | C | A | K, - "Low power condition on"}, - {0x5E, 0x01, D | T | L | P | W | R | S | O | C | A | K, - "Idle condition activated by timer"}, - {0x5E, 0x02, D | T | L | P | W | R | S | O | C | A | K, - "Standby condition activated by timer"}, - {0x5E, 0x03, D | T | L | P | W | R | S | O | C | A | K, - "Idle condition activated by command"}, - {0x5E, 0x04, D | T | L | P | W | R | S | O | C | A | K, - "Standby condition activated by command"}, - {0x5E, 0x41, B, "Power state change to active"}, - {0x5E, 0x42, B, "Power state change to idle"}, - {0x5E, 0x43, B, "Power state change to standby"}, - {0x5E, 0x45, B, "Power state change to sleep"}, - {0x5E, 0x47, B | K, "Power state change to device control"}, - {0x60, 0x00, S, "Lamp failure"}, - {0x61, 0x00, S, "Video acquisition error"}, - {0x61, 0x01, S, "Unable to acquire video"}, - {0x61, 0x02, S, "Out of focus"}, - {0x62, 0x00, S, "Scan head positioning error"}, - {0x63, 0x00, R, "End of user area encountered on this track"}, - {0x63, 0x01, R, "Packet does not fit in available space"}, - {0x64, 0x00, R, "Illegal mode for this track"}, - {0x64, 0x01, R, "Invalid packet size"}, - {0x65, 0x00, SC_ALL_DEVS, "Voltage fault"}, - {0x66, 0x00, S, "Automatic document feeder cover up"}, - {0x66, 0x01, S, "Automatic document feeder lift up"}, - {0x66, 0x02, S, "Document jam in automatic document feeder"}, - {0x66, 0x03, S, "Document miss feed automatic in document feeder"}, - {0x67, 0x00, A, "Configuration failure"}, - {0x67, 0x01, A, "Configuration of incapable logical units failed"}, - {0x67, 0x02, A, "Add logical unit failed"}, - {0x67, 0x03, A, "Modification of logical unit failed"}, - {0x67, 0x04, A, "Exchange of logical unit failed"}, - {0x67, 0x05, A, "Remove of logical unit failed"}, - {0x67, 0x06, A, "Attachment of logical unit failed"}, - {0x67, 0x07, A, "Creation of logical unit failed"}, - {0x67, 0x08, A, "Assign failure occurred"}, - {0x67, 0x09, A, "Multiply assigned logical unit"}, - {0x67, 0x0A, SC_ALL_DEVS, "Set target port groups command failed"}, - {0x68, 0x00, A, SC_LOGICAL_UNIT "not configured"}, - {0x69, 0x00, A, "Data loss on logical unit"}, - {0x69, 0x01, A, "Multiple logical unit failures"}, - {0x69, 0x02, A, "Parity/data mismatch"}, - {0x6A, 0x00, A, "Informational,refer to log"}, - {0x6B, 0x00, A, "State change has occurred"}, - {0x6B, 0x01, A, "Redundancy level got better"}, - {0x6B, 0x02, A, "Redundancy level got worse"}, - {0x6C, 0x00, A, "Rebuild failure occurred"}, - {0x6D, 0x00, A, "Recalculate failure occurred"}, - {0x6E, 0x00, A, "Command to logical unit failed"}, - {0x6F, 0x00, R, - "Copy protection key exchange failure - authentication failure"}, - {0x6F, 0x01, R, - "Copy protection key exchange failure - key not present"}, - {0x6F, 0x02, R, - "Copy protection key exchange failure - key not established"}, - {0x6F, 0x03, R, "Read of scrambled sector without authentication"}, - {0x6F, 0x04, R, - "Media region code is mismatched to logical unit region"}, - {0x6F, 0x05, R, - "Drive region must be permanent/region reset count error"}, - /* - * FIXME(eric) - need a way to represent wildcards here. - */ - {0x70, 0x00, T, "Decompression exception short algorithm id of nn"}, - {0x71, 0x00, T, "Decompression exception long algorithm id"}, - {0x72, 0x00, R, "Session fixation error"}, - {0x72, 0x01, R, "Session fixation error writing lead-in"}, - {0x72, 0x02, R, "Session fixation error writing lead-out"}, - {0x72, 0x03, R, "Session fixation error - incomplete track in session"}, - {0x72, 0x04, R, "Empty or partially written reserved track"}, - {0x72, 0x05, R, "No more track reservations allowed"}, - {0x73, 0x00, R, "Cd control error"}, - {0x73, 0x01, R, "Power calibration area almost full"}, - {0x73, 0x02, R, "Power calibration area is full"}, - {0x73, 0x03, R, "Power calibration area error"}, - {0x73, 0x04, R, "Program memory area update failure"}, - {0x73, 0x05, R, "Program memory area is full"}, - {0x73, 0x06, R, "RMA/PMA is full"}, - {0, 0, 0, NULL} -}; - -static const char *sc_oft_used[0x1f] = { - "umulig", /* index 0x0 should be impossible */ - "Audio play operation ", - "Logical unit ", - "not ready, ", - " operation", - " in progress ", - "Hardware impending failure ", - "Controller impending failure ", - "Data channel impending failure ", /* index 0x8 */ - "Servo impending failure ", - "Spindle impending failure ", - "Firmware impending failure ", - "Recovered data ", - " error rate too high", - " times too high", -}; - -static const char *snstext[] = { - "No Sense", /* There is no sense information */ - "Recovered Error", /* The last command completed successfully - but used error correction */ - "Not Ready", /* The addressed target is not ready */ - "Medium Error", /* Data error detected on the medium */ - "Hardware Error", /* Controller or device failure */ - "Illegal Request", - "Unit Attention", /* Removable medium was changed, or - the target has been reset */ - "Data Protect", /* Access to the data is blocked */ - "Blank Check", /* Reached unexpected written or unwritten - region of the medium */ - "Key=9", /* Vendor specific */ - "Copy Aborted", /* COPY or COMPARE was aborted */ - "Aborted Command", /* The target aborted the command */ - "Equal", /* SEARCH DATA found data equal (obsolete) */ - "Volume Overflow", /* Medium full with still data to be written */ - "Miscompare", /* Source data and data on the medium - do not agree */ - "Key=15" /* Reserved */ -}; - -static -void sg_print_asc_ascq(unsigned char asc, unsigned char ascq) -{ - int k, j; - char obuff[256]; - const char *ccp; - const char *oup; - char c; - int found = 0; - - for (k = 0; additional[k].text; k++) { - if (additional[k].code1 == asc && additional[k].code2 == ascq) { - found = 1; - ccp = additional[k].text; - for (j = 0; *ccp && (j < sizeof(obuff)); ++ccp) { - c = *ccp; - if ((c < 0x20) && (c > 0)) { - oup = sc_oft_used[(int)c]; - if (oup) { - strcpy(obuff + j, oup); - j += strlen(oup); - } else { - strcpy(obuff + j, "???"); - j += 3; - } - } else - obuff[j++] = c; - } - if (j < sizeof(obuff)) - obuff[j] = '\0'; - else - obuff[sizeof(obuff) - 1] = '\0'; - fprintf(OUTP, "Additional sense: %s\n", obuff); - } - } - if (found) - return; - - for (k = 0; additional2[k].text; k++) { - if ((additional2[k].code1 == asc) && - (ascq >= additional2[k].code2_min) && - (ascq <= additional2[k].code2_max)) { - found = 1; - fprintf(OUTP, "Additional sense: "); - fprintf(OUTP, additional2[k].text, ascq); - fprintf(OUTP, "\n"); - } - } - if (!found) - fprintf(OUTP, "ASC=%2x ASCQ=%2x\n", asc, ascq); -} - -/* Print sense information */ -void sg_print_sense(const char *leadin, const unsigned char *sense_buffer, - int sb_len) -{ - int k, s; - int sense_key, sense_class, valid, code; - int descriptor_format = 0; - const char *error = NULL; - - if (sb_len < 1) { - fprintf(OUTP, "sense buffer empty\n"); - return; - } - sense_class = (sense_buffer[0] >> 4) & 0x07; - code = sense_buffer[0] & 0xf; - valid = sense_buffer[0] & 0x80; - if (leadin) - fprintf(OUTP, "%s: ", leadin); - - if (sense_class == 7) { /* extended sense data */ - s = sense_buffer[7] + 8; - if (s > sb_len) { - fprintf(OUTP, - "Sense buffer too small (at %d bytes), %d bytes " - "truncated\n", sb_len, s - sb_len); - s = sb_len; - } - - switch (code) { - case 0x0: - error = "Current"; /* error concerns current command */ - break; - case 0x1: - error = "Deferred"; /* error concerns some earlier command */ - /* e.g., an earlier write to disk cache succeeded, but - now the disk discovers that it cannot write the data */ - break; - case 0x2: - descriptor_format = 1; - error = "Descriptor current"; - /* new descriptor sense format */ - break; - case 0x3: - descriptor_format = 1; - error = "Descriptor deferred"; - /* new descriptor sense format (deferred report) */ - break; - default: - error = "Invalid"; - } - sense_key = sense_buffer[descriptor_format ? 1 : 2] & 0xf; - fprintf(OUTP, "%s, Sense key: %s\n", error, snstext[sense_key]); - - if (descriptor_format) - sg_print_asc_ascq(sense_buffer[2], sense_buffer[3]); - else { - if (!valid) - fprintf(OUTP, "[valid=0] "); - fprintf(OUTP, "Info fld=0x%x, ", - (int)((sense_buffer[3] << 24) | - (sense_buffer[4] << 16) | (sense_buffer[5] - << 8) | - sense_buffer[6])); - - if (sense_buffer[2] & 0x80) - fprintf(OUTP, "FMK "); /* current command has read a filemark */ - if (sense_buffer[2] & 0x40) - fprintf(OUTP, "EOM "); /* end-of-medium condition exists */ - if (sense_buffer[2] & 0x20) - fprintf(OUTP, "ILI "); /* incorrect block length requested */ - - if (s > 13) { - if (sense_buffer[12] || sense_buffer[13]) - sg_print_asc_ascq(sense_buffer[12], - sense_buffer[13]); - } - if (sense_key == 5 && s >= 18 - && (sense_buffer[15] & 0x80)) { - fprintf(OUTP, - "Sense Key Specific: Error in %s byte %d", - (sense_buffer[15] & 0x40) ? "Command" : - "Data", - (sense_buffer[16] << 8) | - sense_buffer[17]); - if (sense_buffer[15] & 0x08) { - fprintf(OUTP, " bit %d\n", - sense_buffer[15] & 0x07); - } else { - fprintf(OUTP, "\n"); - } - } - } - - } else { /* non-extended sense data */ - - /* - * Standard says: - * sense_buffer[0] & 0200 : address valid - * sense_buffer[0] & 0177 : vendor-specific error code - * sense_buffer[1] & 0340 : vendor-specific - * sense_buffer[1..3] : 21-bit logical block address - */ - - if (sb_len < 4) { - fprintf(OUTP, - "sense buffer too short (4 byte minimum)\n"); - return; - } - if (leadin) - fprintf(OUTP, "%s: ", leadin); - if (sense_buffer[0] < 15) - fprintf(OUTP, - "old sense: key %s\n", - snstext[sense_buffer[0] & 0x0f]); - else - fprintf(OUTP, "sns = %2x %2x\n", sense_buffer[0], - sense_buffer[2]); - - fprintf(OUTP, "Non-extended sense class %d code 0x%0x ", - sense_class, code); - s = 4; - } - - fprintf(OUTP, "Raw sense data (in hex):\n "); - for (k = 0; k < s; ++k) { - if ((k > 0) && (0 == (k % 24))) - fprintf(OUTP, "\n "); - fprintf(OUTP, "%02x ", sense_buffer[k]); - } - fprintf(OUTP, "\n"); -} - -static const char *hostbyte_table[] = { - "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", - "DID_BAD_TARGET", - "DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR", - "DID_PASSTHROUGH", "DID_SOFT_ERROR", NULL -}; - -void sg_print_host_status(int host_status) -{ - static int maxcode = 0; - int i; - - if (!maxcode) { - for (i = 0; hostbyte_table[i]; i++) ; - maxcode = i - 1; - } - fprintf(OUTP, "Host_status=0x%02x", host_status); - if (host_status > maxcode) { - fprintf(OUTP, "is invalid "); - return; - } - fprintf(OUTP, "(%s) ", hostbyte_table[host_status]); -} - -static const char *driverbyte_table[] = { - "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", - "DRIVER_ERROR", - "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE", NULL -}; - -static const char *driversuggest_table[] = { "SUGGEST_OK", - "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE", - unknown, unknown, unknown, "SUGGEST_SENSE", NULL -}; - -void sg_print_driver_status(int driver_status) -{ - static int driver_max = 0, suggest_max = 0; - int i; - int dr = driver_status & SG_ERR_DRIVER_MASK; - int su = (driver_status & SG_ERR_SUGGEST_MASK) >> 4; - - if (!driver_max) { - for (i = 0; driverbyte_table[i]; i++) ; - driver_max = i; - for (i = 0; driversuggest_table[i]; i++) ; - suggest_max = i; - } - fprintf(OUTP, "Driver_status=0x%02x", driver_status); - fprintf(OUTP, " (%s,%s) ", - dr < driver_max ? driverbyte_table[dr] : "invalid", - su < suggest_max ? driversuggest_table[su] : "invalid"); -} - -static int sg_sense_print(const char *leadin, int scsi_status, - int host_status, int driver_status, - const unsigned char *sense_buffer, int sb_len) -{ - int done_leadin = 0; - int done_sense = 0; - - scsi_status &= 0x7e; /*sanity */ - if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status)) - return 1; /* No problems */ - if (0 != scsi_status) { - if (leadin) - fprintf(OUTP, "%s: ", leadin); - done_leadin = 1; - fprintf(OUTP, "scsi status: "); - sg_print_scsi_status(scsi_status); - fprintf(OUTP, "\n"); - if (sense_buffer && ((scsi_status == SCSI_CHECK_CONDITION) || - (scsi_status == - SCSI_COMMAND_TERMINATED))) { - sg_print_sense(0, sense_buffer, sb_len); - done_sense = 1; - } - } - if (0 != host_status) { - if (leadin && (!done_leadin)) - fprintf(OUTP, "%s: ", leadin); - if (done_leadin) - fprintf(OUTP, "plus...: "); - else - done_leadin = 1; - sg_print_host_status(host_status); - fprintf(OUTP, "\n"); - } - if (0 != driver_status) { - if (leadin && (!done_leadin)) - fprintf(OUTP, "%s: ", leadin); - if (done_leadin) - fprintf(OUTP, "plus...: "); - else - done_leadin = 1; - sg_print_driver_status(driver_status); - fprintf(OUTP, "\n"); - if (sense_buffer && (!done_sense) && - (SG_ERR_DRIVER_SENSE == (0xf & driver_status))) - sg_print_sense(0, sense_buffer, sb_len); - } - return 0; -} - -#ifdef SG_IO -int sg_chk_n_print3(const char *leadin, struct sg_io_hdr *hp) -{ - return sg_sense_print(leadin, hp->status, hp->host_status, - hp->driver_status, hp->sbp, hp->sb_len_wr); -} -#endif - -int sg_chk_n_print(const char *leadin, int masked_status, - int host_status, int driver_status, - const unsigned char *sense_buffer, int sb_len) -{ - int scsi_status = (masked_status << 1) & 0x7e; - - return sg_sense_print(leadin, scsi_status, host_status, driver_status, - sense_buffer, sb_len); -} - -#ifdef SG_IO -int sg_err_category3(struct sg_io_hdr *hp) -{ - return sg_err_category_new(hp->status, hp->host_status, - hp->driver_status, hp->sbp, hp->sb_len_wr); -} -#endif - -int sg_err_category(int masked_status, int host_status, - int driver_status, const unsigned char *sense_buffer, - int sb_len) -{ - int scsi_status = (masked_status << 1) & 0x7e; - - return sg_err_category_new(scsi_status, host_status, driver_status, - sense_buffer, sb_len); -} - -int sg_err_category_new(int scsi_status, int host_status, int driver_status, - const unsigned char *sense_buffer, int sb_len) -{ - scsi_status &= 0x7e; - if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status)) - return SG_ERR_CAT_CLEAN; - if ((SCSI_CHECK_CONDITION == scsi_status) || - (SCSI_COMMAND_TERMINATED == scsi_status) || - (SG_ERR_DRIVER_SENSE == (0xf & driver_status))) { - if (sense_buffer && (sb_len > 2)) { - int sense_key; - unsigned char asc; - - if (sense_buffer[0] & 0x2) { - sense_key = sense_buffer[1] & 0xf; - asc = sense_buffer[2]; - } else { - sense_key = sense_buffer[2] & 0xf; - asc = (sb_len > 12) ? sense_buffer[12] : 0; - } - - if (RECOVERED_ERROR == sense_key) - return SG_ERR_CAT_RECOVERED; - else if (UNIT_ATTENTION == sense_key) { - if (0x28 == asc) - return SG_ERR_CAT_MEDIA_CHANGED; - if (0x29 == asc) - return SG_ERR_CAT_RESET; - } - } - return SG_ERR_CAT_SENSE; - } - if (0 != host_status) { - if ((SG_ERR_DID_NO_CONNECT == host_status) || - (SG_ERR_DID_BUS_BUSY == host_status) || - (SG_ERR_DID_TIME_OUT == host_status)) - return SG_ERR_CAT_TIMEOUT; - } - if (0 != driver_status) { - if (SG_ERR_DRIVER_TIMEOUT == driver_status) - return SG_ERR_CAT_TIMEOUT; - } - return SG_ERR_CAT_OTHER; -} - -int sg_get_command_size(unsigned char opcode) -{ - return COMMAND_SIZE(opcode); -} - -void sg_get_command_name(unsigned char opcode, int buff_len, char *buff) -{ - const char **table = commands[group(opcode)]; - - if ((NULL == buff) || (buff_len < 1)) - return; - - switch ((unsigned long)table) { - case RESERVED_GROUP: - strncpy(buff, reserved, buff_len); - break; - case VENDOR_GROUP: - strncpy(buff, vendor, buff_len); - break; - default: - strncpy(buff, table[opcode & 0x1f], buff_len); - break; - } -} diff --git a/testcases/kernel/fs/scsi/ltpscsi/sg_err.h b/testcases/kernel/fs/scsi/ltpscsi/sg_err.h deleted file mode 100755 index 735f8a94..00000000 --- a/testcases/kernel/fs/scsi/ltpscsi/sg_err.h +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef SG_ERR_H -#define SG_ERR_H - -/* Feel free to copy and modify this GPL-ed code into your applications. */ - -/* Version 0.89 (20030313) -*/ - - -/* Some of the following error/status codes are exchanged between the - various layers of the SCSI sub-system in Linux and should never - reach the user. They are placed here for completeness. What appears - here is copied from drivers/scsi/scsi.h which is not visible in - the user space. */ - -#ifndef SCSI_CHECK_CONDITION -/* Following are the "true" SCSI status codes. Linux has traditionally - used a 1 bit right and masked version of these. So now CHECK_CONDITION - and friends (in ) are deprecated. */ -#define SCSI_CHECK_CONDITION 0x2 -#define SCSI_CONDITION_MET 0x4 -#define SCSI_BUSY 0x8 -#define SCSI_IMMEDIATE 0x10 -#define SCSI_IMMEDIATE_CONDITION_MET 0x14 -#define SCSI_RESERVATION_CONFLICT 0x18 -#define SCSI_COMMAND_TERMINATED 0x22 -#define SCSI_TASK_SET_FULL 0x28 -#define SCSI_ACA_ACTIVE 0x30 -#define SCSI_TASK_ABORTED 0x40 -#endif - -/* The following are 'host_status' codes */ -#ifndef DID_OK -#define DID_OK 0x00 -#endif -#ifndef DID_NO_CONNECT -#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ -#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ -#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ -#define DID_BAD_TARGET 0x04 /* Bad target (id?) */ -#define DID_ABORT 0x05 /* Told to abort for some other reason */ -#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */ -#define DID_ERROR 0x07 /* Internal error */ -#define DID_RESET 0x08 /* Reset by somebody */ -#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */ -#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */ -#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */ -#endif - -/* These defines are to isolate applictaions from kernel define changes */ -#define SG_ERR_DID_OK DID_OK -#define SG_ERR_DID_NO_CONNECT DID_NO_CONNECT -#define SG_ERR_DID_BUS_BUSY DID_BUS_BUSY -#define SG_ERR_DID_TIME_OUT DID_TIME_OUT -#define SG_ERR_DID_BAD_TARGET DID_BAD_TARGET -#define SG_ERR_DID_ABORT DID_ABORT -#define SG_ERR_DID_PARITY DID_PARITY -#define SG_ERR_DID_ERROR DID_ERROR -#define SG_ERR_DID_RESET DID_RESET -#define SG_ERR_DID_BAD_INTR DID_BAD_INTR -#define SG_ERR_DID_PASSTHROUGH DID_PASSTHROUGH -#define SG_ERR_DID_SOFT_ERROR DID_SOFT_ERROR - -/* The following are 'driver_status' codes */ -#ifndef DRIVER_OK -#define DRIVER_OK 0x00 -#endif -#ifndef DRIVER_BUSY -#define DRIVER_BUSY 0x01 -#define DRIVER_SOFT 0x02 -#define DRIVER_MEDIA 0x03 -#define DRIVER_ERROR 0x04 -#define DRIVER_INVALID 0x05 -#define DRIVER_TIMEOUT 0x06 -#define DRIVER_HARD 0x07 -#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ - -/* Following "suggests" are "or-ed" with one of previous 8 entries */ -#define SUGGEST_RETRY 0x10 -#define SUGGEST_ABORT 0x20 -#define SUGGEST_REMAP 0x30 -#define SUGGEST_DIE 0x40 -#define SUGGEST_SENSE 0x80 -#define SUGGEST_IS_OK 0xff -#endif -#ifndef DRIVER_MASK -#define DRIVER_MASK 0x0f -#endif -#ifndef SUGGEST_MASK -#define SUGGEST_MASK 0xf0 -#endif - -/* These defines are to isolate applictaions from kernel define changes */ -#define SG_ERR_DRIVER_OK DRIVER_OK -#define SG_ERR_DRIVER_BUSY DRIVER_BUSY -#define SG_ERR_DRIVER_SOFT DRIVER_SOFT -#define SG_ERR_DRIVER_MEDIA DRIVER_MEDIA -#define SG_ERR_DRIVER_ERROR DRIVER_ERROR -#define SG_ERR_DRIVER_INVALID DRIVER_INVALID -#define SG_ERR_DRIVER_TIMEOUT DRIVER_TIMEOUT -#define SG_ERR_DRIVER_HARD DRIVER_HARD -#define SG_ERR_DRIVER_SENSE DRIVER_SENSE -#define SG_ERR_SUGGEST_RETRY SUGGEST_RETRY -#define SG_ERR_SUGGEST_ABORT SUGGEST_ABORT -#define SG_ERR_SUGGEST_REMAP SUGGEST_REMAP -#define SG_ERR_SUGGEST_DIE SUGGEST_DIE -#define SG_ERR_SUGGEST_SENSE SUGGEST_SENSE -#define SG_ERR_SUGGEST_IS_OK SUGGEST_IS_OK -#define SG_ERR_DRIVER_MASK DRIVER_MASK -#define SG_ERR_SUGGEST_MASK SUGGEST_MASK - - - -/* The following "print" functions send ACSII to stdout */ -extern void sg_print_command(const unsigned char * command); -extern void sg_print_sense(const char * leadin, - const unsigned char * sense_buffer, int sb_len); -extern void sg_print_status(int masked_status); -extern void sg_print_scsi_status(int scsi_status); -extern void sg_print_host_status(int host_status); -extern void sg_print_driver_status(int driver_status); - -/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings - else it prints to standard output and returns 0. */ -extern int sg_chk_n_print(const char * leadin, int masked_status, - int host_status, int driver_status, - const unsigned char * sense_buffer, int sb_len); - -/* The following function declaration is for the sg version 3 driver. - Only version 3 sg_err.c defines it. */ -struct sg_io_hdr; -extern int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp); - - -/* The following "category" function returns one of the following */ -#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */ -#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */ -#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */ -#define SG_ERR_CAT_TIMEOUT 3 -#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ -#define SG_ERR_CAT_SENSE 98 /* Something else is in the sense buffer */ -#define SG_ERR_CAT_OTHER 99 /* Some other error/warning has occurred */ - -extern int sg_err_category(int masked_status, int host_status, - int driver_status, const unsigned char * sense_buffer, - int sb_len); - -extern int sg_err_category_new(int scsi_status, int host_status, - int driver_status, const unsigned char * sense_buffer, - int sb_len); - -/* The following function declaration is for the sg version 3 driver. - Only version 3 sg_err.c defines it. */ -extern int sg_err_category3(struct sg_io_hdr * hp); - -/* Returns length of SCSI command given the opcode (first byte) */ -extern int sg_get_command_size(unsigned char opcode); - -extern void sg_get_command_name(unsigned char opcode, int buff_len, - char * buff); - -#endif diff --git a/testcases/kernel/fs/scsi/ltpscsi/sg_include.h b/testcases/kernel/fs/scsi/ltpscsi/sg_include.h deleted file mode 100755 index 5baca3cc..00000000 --- a/testcases/kernel/fs/scsi/ltpscsi/sg_include.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifdef SG_KERNEL_INCLUDES - #include "/usr/src/linux/include/scsi/sg.h" - #include "/usr/src/linux/include/scsi/scsi.h" -#else - #ifdef SG_TRICK_GNU_INCLUDES - #include - #include - #else - #include - #include - #endif -#endif - -/* - Getting the correct include files for the sg interface can be an ordeal. - In a perfect world, one would just write: - #include - #include - This would include the files found in the /usr/include/scsi directory. - Those files are maintained with the GNU library which may or may not - agree with the kernel and version of sg driver that is running. Any - many cases this will not matter. However in some it might, for example - glibc 2.1's include files match the sg driver found in the lk 2.2 - series. Hence if glibc 2.1 is used with lk 2.4 then the additional - sg v3 interface will not be visible. - If this is a problem then defining SG_KERNEL_INCLUDES will access the - kernel supplied header files (assuming they are in the normal place). - The GNU library maintainers and various kernel people don't like - this approach (but it does work). - The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and - was used) prior to glibc 2.2 . Prior to that version /usr/include/linux - was a symbolic link to /usr/src/linux/include/linux . - - There are other approaches if this include "mixup" causes pain. These - would involve include files being copied or symbolic links being - introduced. - - Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES - nor SG_TRICK_GNU_INCLUDES is defined. - - dpg 20010415 -*/ diff --git a/testcases/kernel/fs/squashfs/squashfs01.c b/testcases/kernel/fs/squashfs/squashfs01.c index 502de419..fbcb7658 100755 --- a/testcases/kernel/fs/squashfs/squashfs01.c +++ b/testcases/kernel/fs/squashfs/squashfs01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Kernel commits * * - f37aa4c7366 (squashfs: add more sanity checks in id lookup) diff --git a/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug05.txt b/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug05.txt index 26fd5961..67f8198d 100755 --- a/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug05.txt +++ b/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug05.txt @@ -1,35 +1,29 @@ -# Test Case 5 - Pseudocode +Testcase 05 +----------- -# This test looks for memory leaks or deadlocks +It's been found that sometimes onlining and offlining CPUs confuse some +of the various system tools. We found that sar wouldn't register the change +in newly available cpus that weren't there when it started. This +test case seeks to exercise this known error cases and verify that +they behave correctly now. -# "mm_struct slab leak (affected only some architectures)" +Algorithm - Sar +=============== +Given a CPU to test that exists -INTERVAL=30 -THRESHHOLD='xxx' +Make sure the specified cpu is offline -# TODO: Start monitoring memory usage via vmstat and sar +Loop until done: + Start up sar writing to a temp log and give it a little time to run -# TODO: Start dbt2, running for at least 4 hours + Verify that SAR has correctly displayed all fields of CPU statistics + as '0.00' for the offlined CPU or just not displayed it in its tmp log -while [ 1 ]; do - last if workload has completed + Online the specified cpu - select a cpu at random - if cpu is online - offline it - else - online it - fi + Take another timestamp and another count of offlined CPUs - measure current throughput - # TODO: Mary and Mark will better define how to detect - # the threshhold and what to do in response - if [ throughput falls below $THRESHHOLD ]; then - echo "Throughput has fallen below threshhold." - fi + Verify SAR registered the change in CPU online/offline states - sleep $INTERVAL -done - -# Analyze system statistics to determine memory leaks -# Analyze drops in activities +When exiting: + Kill the sar process diff --git a/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug06.txt b/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug06.txt index 0cccc871..d7d6c181 100755 --- a/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug06.txt +++ b/testcases/kernel/hotplug/cpu_hotplug/doc/hotplug06.txt @@ -2,11 +2,9 @@ Testcase 06 ----------- It's been found that sometimes onlining and offlining CPUs confuse some -of the various system tools. In particular, we found it caused top to -crash, and found that sar wouldn't register newly available cpus that -weren't there when it started. This test case seeks to exercise these -known error cases and verify that they behave correctly now. - +of the various system tools. We found it caused top to +crash. This test case seeks to exercise this known error cases and +verify that they behave correctly now. Algorithm - Top =============== @@ -29,28 +27,3 @@ When exiting: Restore all CPUs to their initial state -Algorithm - Sar -=============== -Given a CPU to test that exists - -Make sure the specified cpu is offline - -Loop until done: - Start up sar writing to a temp log and give it a little time to run - - Verify that SAR has correctly listed the missing CPU as 'nan' in its - tmp log - - Take a timestamp and count how many CPUs sar is reporting to be - offline - - Online the specified cpu - - Take another timestamp and another count of offlined CPUs. - - Verify that the number of CPUs offline has changed - -When exiting: - Kill the sar process - - diff --git a/testcases/kernel/hotplug/cpu_hotplug/functional/cpuhotplug06.sh b/testcases/kernel/hotplug/cpu_hotplug/functional/cpuhotplug06.sh index b9c1889d..d5267719 100755 --- a/testcases/kernel/hotplug/cpu_hotplug/functional/cpuhotplug06.sh +++ b/testcases/kernel/hotplug/cpu_hotplug/functional/cpuhotplug06.sh @@ -53,7 +53,7 @@ if tst_virt_hyperv; then tst_brkm TCONF "Microsoft Hyper-V detected, no support for CPU hotplug" fi -if top -v | grep -q htop; then +if top -h | grep -q htop; then tst_brkm TCONF "htop is used instead of top (workaround: alias top='/path/to/real/top')" fi diff --git a/testcases/kernel/include/numa_helper.h b/testcases/kernel/include/numa_helper.h index ed45ec8a..e67ddaac 100755 --- a/testcases/kernel/include/numa_helper.h +++ b/testcases/kernel/include/numa_helper.h @@ -27,13 +27,34 @@ # include #endif +struct tst_cg_group; + #define NH_MEMS (1 << 0) #define NH_CPUS (1 << 1) +#if defined(__powerpc__) || defined(__powerpc64__) +# define MAXNODES 256 +#else +# define MAXNODES 512 +#endif + +#define TESTMEM (1UL<<30) + +#define BITS_PER_LONG (8 * sizeof(long)) + +#define PATH_SYS_SYSTEM "/sys/devices/system" + +static inline void set_node(unsigned long *array, unsigned int node) +{ + array[node / BITS_PER_LONG] |= 1UL << (node % BITS_PER_LONG); +} + unsigned long get_max_node(void); int get_allowed_nodes_arr(int flag, int *num_nodes, int **nodes); int get_allowed_nodes(int flag, int count, ...); void nh_dump_nodes(void); int is_numa(void (*cleanup_fn)(void), int flag, int min_nodes); +void write_node_cpusets(const struct tst_cg_group *cg, long nd); + #endif /* NUMA_HELPER_H */ diff --git a/testcases/kernel/input/Makefile b/testcases/kernel/input/Makefile index 03225444..945299a1 100755 --- a/testcases/kernel/input/Makefile +++ b/testcases/kernel/input/Makefile @@ -3,10 +3,10 @@ top_srcdir ?= ../../.. +LTPLIBS = uinput + include $(top_srcdir)/include/mk/testcases.mk -FILTER_OUT_MAKE_TARGETS := input_helper +LDLIBS += -lltpuinput include $(top_srcdir)/include/mk/generic_leaf_target.mk - -$(MAKE_TARGETS): %: input_helper.o diff --git a/testcases/kernel/input/input01.c b/testcases/kernel/input/input01.c index 95db3f43..e57b041e 100755 --- a/testcases/kernel/input/input01.c +++ b/testcases/kernel/input/input01.c @@ -1,187 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda - * - * 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 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ - /* - * Create a virtual device (mouse), send events to /dev/uinput - * and check that the events are well received in /dev/input/eventX - */ +/*\ + * Verify that /dev/input/eventX receive events sent from a virtual device, + * that in our case is a mouse. + */ -#include +#include "input_common.h" -#include "input_helper.h" -#include "test.h" -#include "safe_macros.h" -#include "lapi/fcntl.h" +#define NUM_EVENTS 20 +#define MOVE_X 10 +#define MOVE_Y 1 -#define NB_TEST 20 +static int fd_send = -1; +static int fd_recv = -1; -static void setup(void); -static void send_events(void); -static int verify_data(struct input_event *iev, int nb); -static int check_events(void); -static void cleanup(void); - -static int fd; -static int fd2; - -char *TCID = "input01"; - -int main(int ac, char **av) +static void run(void) { - int lc; - int pid; + struct input_event iev[3]; - tst_parse_opts(ac, av, NULL, NULL); + tst_res(TINFO, "Sending relative move: (%i, %i)", MOVE_X, MOVE_Y); - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - pid = tst_fork(); - - switch (pid) { - case 0: - send_events(); - exit(0); - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); - default: - if (check_events()) - tst_resm(TFAIL, "Wrong data read from eventX"); - else - tst_resm(TPASS, "Data received from eventX"); - break; - } - - SAFE_WAITPID(NULL, pid, NULL, 0); + for (int i = 0; i < NUM_EVENTS; i++) { + send_relative_move(fd_send, MOVE_X, MOVE_Y); + usleep(1000); } - cleanup(); - tst_exit(); + tst_res(TINFO, "Reading events back"); + + for (int i = 0; i < NUM_EVENTS; i++) { + SAFE_READ(0, fd_recv, iev, 3 * sizeof(struct input_event)); + + TST_EXP_EQ_LI(iev[0].type, EV_REL); + TST_EXP_EQ_LI(iev[0].code, REL_X); + TST_EXP_EQ_LI(iev[0].value, MOVE_X); + + TST_EXP_EQ_LI(iev[1].type, EV_REL); + TST_EXP_EQ_LI(iev[1].code, REL_Y); + TST_EXP_EQ_LI(iev[1].value, MOVE_Y); + + TST_EXP_EQ_LI(iev[2].type, EV_SYN); + TST_EXP_EQ_LI(iev[2].code, 0); + TST_EXP_EQ_LI(iev[2].value, 0); + } } static void setup(void) { - tst_require_root(); + fd_send = open_uinput(); + if (fd_send == -1) + tst_brk(TCONF, "Virtual device is not available"); - fd = open_uinput(); - setup_mouse_events(fd); - create_device(fd); + setup_mouse_events(fd_send); + create_input_device(fd_send); - fd2 = open_device(); -} - -static void send_events(void) -{ - int nb; - - for (nb = 0; nb < NB_TEST; ++nb) { - send_rel_move(fd, 10, 1); - usleep(1000); - } -} - -static int check_events(void) -{ - int nb, rd; - unsigned int i; - struct input_event iev[64]; - - nb = 0; - - while (nb < NB_TEST * 3) { - rd = read(fd2, iev, sizeof(iev)); - - if (rd < 0) - tst_brkm(TBROK | TERRNO, cleanup, "read()"); - - if (rd == 0 || rd % sizeof(struct input_event)) { - tst_resm(TINFO, "read() returned unexpected %i", rd); - return 1; - } - - for (i = 0; i < rd / sizeof(struct input_event); i++) { - if (verify_data(&iev[i], nb++)) - return 1; - } - } - - return 0; -} - -static int verify_data(struct input_event *iev, int nb) -{ - if (nb % 3 == 0) { - if (iev->type != EV_REL) { - tst_resm(TINFO, - "%i: Unexpected event type %i expected %i", - nb, iev->type, EV_REL); - return 1; - } - - if (iev->code != REL_X) - return 1; - - if (iev->value != 10) - return 1; - - return 0; - } - - if (nb % 3 == 1) { - if (iev->type != EV_REL) { - tst_resm(TINFO, - "%i: Unexpected event type %i expected %i", - nb, iev->type, EV_REL); - return 1; - } - - if (iev->code != REL_Y) - return 1; - - if (iev->value != 1) - return 1; - - return 0; - } - - if (nb % 3 == 2) { - if (iev->type != EV_SYN) { - tst_resm(TINFO, - "%i: Unexpected event type %i expected %i", - nb, iev->type, EV_SYN); - return 1; - } - - if (iev->code != 0) - return 1; - - if (iev->value != 0) - return 1; - - return 0; - } - return 1; + fd_recv = open_event_device(); } static void cleanup(void) { - if (fd2 > 0 && close(fd2)) - tst_resm(TWARN | TERRNO, "close(fd2)"); + if (fd_send != -1) + destroy_input_device(fd_send); - destroy_device(fd); + if (fd_recv != -1) + SAFE_CLOSE(fd_recv); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, +}; diff --git a/testcases/kernel/input/input02.c b/testcases/kernel/input/input02.c index 6964ed70..f4f6543a 100755 --- a/testcases/kernel/input/input02.c +++ b/testcases/kernel/input/input02.c @@ -1,106 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda - * - * 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 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ - /* - * Create a virtual device (mouse), send events to /dev/uinput - * and check that the events are not received in /dev/input/eventX - * because the device is grabbed by another process - */ +/*\ + * Verify that /dev/input/eventX won't receive any event sent from a virtual + * device, that in our case is a mouse, when the event device has been grabbed + * by an another process. + */ -#include +#include "input_common.h" -#include "test.h" -#include "safe_macros.h" -#include "lapi/fcntl.h" -#include "input_helper.h" +#define MOVE_X 10 +#define MOVE_Y 1 -#define NB_TEST 20 +static int fd_send = -1; +static int fd_recv = -1; -static void setup(void); -static void send_information(void); -static void cleanup(void); - -static int fd; -static int fd2; - -char *TCID = "input02"; - -int main(int ac, char **av) +static void send_events(void) { - int lc; - int pid; + int fd; - tst_parse_opts(ac, av, NULL, NULL); + fd = open_event_device(); - setup(); + SAFE_IOCTL(fd, EVIOCGRAB, 1); + tst_res(TINFO, "The virtual device was grabbed"); - for (lc = 0; TEST_LOOPING(lc); ++lc) { - pid = tst_fork(); + send_relative_move(fd_send, MOVE_X, MOVE_Y); - fd2 = open_device(); + TST_CHECKPOINT_WAKE_AND_WAIT(0); - switch (pid) { - case 0: - send_information(); - exit(0); - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); - default: - if (no_events_queued(fd2, 0)) - tst_resm(TPASS, "No data received in eventX"); - else - tst_resm(TFAIL, "Data received in eventX"); - SAFE_CLOSE(NULL, fd2); - break; - } + SAFE_CLOSE(fd); +} - SAFE_WAITPID(NULL, pid, NULL, 0); +static void run(void) +{ + if (!SAFE_FORK()) { + send_events(); + exit(0); } - cleanup(); - tst_exit(); + TST_CHECKPOINT_WAIT(0); + + verify_no_events_queued(fd_recv); + + TST_CHECKPOINT_WAKE(0); } static void setup(void) { - tst_require_root(); + fd_send = open_uinput(); + if (fd_send == -1) + tst_brk(TCONF, "Virtual device is not available"); - fd = open_uinput(); - setup_mouse_events(fd); - create_device(fd); -} + setup_mouse_events(fd_send); + create_input_device(fd_send); -static void send_information(void) -{ - int nb; - - SAFE_IOCTL(NULL, fd2, EVIOCGRAB, 1); - tst_resm(TINFO, "The virtual device was grabbed"); - - for (nb = 0; nb < NB_TEST; ++nb) { - send_rel_move(fd, 10, 1); - usleep(1000); - } - - SAFE_CLOSE(NULL, fd2); + fd_recv = open_event_device(); } static void cleanup(void) { - destroy_device(fd); + if (fd_send != -1) + destroy_input_device(fd_send); + + if (fd_recv != -1) + SAFE_CLOSE(fd_recv); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .needs_root = 1, + .needs_checkpoints = 1, +}; diff --git a/testcases/kernel/input/input03.c b/testcases/kernel/input/input03.c index 6cd753d0..5229379c 100755 --- a/testcases/kernel/input/input03.c +++ b/testcases/kernel/input/input03.c @@ -1,145 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda - * - * 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 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ - /* - * Create a virtual device (mouse), send events to /dev/uinput - * and check that the events are well received in /dev/input/mice - */ +/*\ + * Verify that /dev/input/mice receive events sent from a virtual device, + * that in our case is a mouse. The events are a sequence of mouse right click. + */ -#include #include -#include "test.h" -#include "safe_macros.h" -#include "lapi/fcntl.h" -#include "input_helper.h" +#include "input_common.h" -#define NB_TEST 10 +#define NUM_EVENTS 10 #define PS2_RIGHT_BTN 0x02 +#define MOUSE_DEV "/dev/input/mice" -static void setup(void); -static void send_events(void); -static int check_events(void); -static void cleanup(void); +static int fd_send = -1; +static int fd_recv = -1; -static int fd, fd2; - -char *TCID = "input03"; - -int main(int ac, char **av) +static void recv_data(void) { - int lc; - int pid; + tst_res(TINFO, "Reading events back"); - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - pid = tst_fork(); - - switch (pid) { - case 0: - send_events(); - exit(0); - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); - default: - if (check_events()) - tst_resm(TFAIL, "Wrong data received"); - else - tst_resm(TPASS, - "Data received in /dev/input/mice"); - break; - } - - SAFE_WAITPID(NULL, pid, NULL, 0); - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_require_root(); - - fd = open_uinput(); - - setup_mouse_events(fd); - SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_KEY); - SAFE_IOCTL(NULL, fd, UI_SET_KEYBIT, BTN_RIGHT); - - create_device(fd); - - fd2 = SAFE_OPEN(NULL, "/dev/input/mice", O_RDONLY); -} - -static void send_events(void) -{ - int nb; - - for (nb = 0; nb < NB_TEST; ++nb) { - send_event(fd, EV_KEY, BTN_RIGHT, 1); - send_event(fd, EV_SYN, 0, 0); - usleep(1000); - send_event(fd, EV_KEY, BTN_RIGHT, 0); - send_event(fd, EV_SYN, 0, 0); - usleep(1000); - } -} - -static int check_events(void) -{ - int nb, rd, i, pressed = 0; char buf[30]; + int events = 0; + int pressed = 0; + int num_bytes = 0; - nb = 0; + TST_CHECKPOINT_WAKE(0); - while (nb < NB_TEST) { - rd = read(fd2, buf, sizeof(buf)); + while (events < NUM_EVENTS) { + memset(buf, 0, sizeof(buf)); - if (rd < 0) - tst_brkm(TBROK | TERRNO, NULL, "read() failed"); + num_bytes = SAFE_READ(0, fd_recv, buf, sizeof(buf)); - if (rd % 3) { - tst_resm(TINFO, "read() returned %i", rd); - return 1; - } - - for (i = 0; i < rd / 3; i++) { + for (int i = 0; i < num_bytes / 3; i++) { if (buf[3*i] & PS2_RIGHT_BTN) pressed = 1; if (pressed == 1 && !(buf[3*i] & PS2_RIGHT_BTN)) { pressed = 0; - nb++; + events++; } } } - return nb != NB_TEST; + TST_EXP_EQ_LI(events, NUM_EVENTS); +} + +static void send_mouse_events(void) +{ + tst_res(TINFO, "Sending right click"); + + TST_CHECKPOINT_WAIT(0); + + for (int i = 0; i < NUM_EVENTS; i++) { + send_event(fd_send, EV_KEY, BTN_RIGHT, 1); + send_event(fd_send, EV_SYN, 0, 0); + usleep(1000); + + send_event(fd_send, EV_KEY, BTN_RIGHT, 0); + send_event(fd_send, EV_SYN, 0, 0); + usleep(1000); + } +} + +static void run(void) +{ + if (!SAFE_FORK()) { + send_mouse_events(); + exit(0); + } + + recv_data(); +} + +static void setup(void) +{ + fd_send = open_uinput(); + if (fd_send == -1) + tst_brk(TCONF, "Virtual device is not available"); + + setup_mouse_events(fd_send); + SAFE_IOCTL(fd_send, UI_SET_EVBIT, EV_KEY); + SAFE_IOCTL(fd_send, UI_SET_KEYBIT, BTN_RIGHT); + + create_input_device(fd_send); + + fd_recv = SAFE_OPEN(MOUSE_DEV, O_RDONLY); } static void cleanup(void) { - if (fd2 > 0 && close(fd2)) - tst_resm(TWARN, "close(fd2) failed"); + if (fd_send != -1) + destroy_input_device(fd_send); - destroy_device(fd); + if (fd_recv != -1) + SAFE_CLOSE(fd_recv); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .needs_root = 1, + .needs_checkpoints = 1, +}; diff --git a/testcases/kernel/input/input04.c b/testcases/kernel/input/input04.c index e57b76b0..0f05404c 100755 --- a/testcases/kernel/input/input04.c +++ b/testcases/kernel/input/input04.c @@ -1,103 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda - * - * 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 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ - /* - * Create a virtual device (mouse), send empty events to /dev/uinput - * and check that the events are not received in /dev/inputX - */ +/*\ + * Verify that /dev/input/eventX doesn't receive any event sent from a virtual + * device, that in our case is a mouse, when relative move is (0, 0) + */ -#include +#include "input_common.h" -#include "test.h" -#include "safe_macros.h" -#include "lapi/fcntl.h" -#include "input_helper.h" +#define NUM_EVENTS 20 -#define NB_TEST 20 +static int fd_send = -1; +static int fd_recv = -1; -static void setup(void); -static void send_events(void); -static void cleanup(void); - -static int fd, fd2; - -char *TCID = "input04"; - -int main(int ac, char **av) +static void run(void) { - int lc; - int pid; + tst_res(TINFO, "Sending empty relative move"); - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - pid = tst_fork(); - - switch (pid) { - case 0: - send_events(); - exit(0); - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); - default: - if (no_events_queued(fd2, 1)) - tst_resm(TPASS, - "No data received in /dev/inputX"); - else - tst_resm(TFAIL, - "Data received /dev/inputX"); - break; - } - - SAFE_WAITPID(NULL, pid, NULL, 0); + for (int i = 0; i < NUM_EVENTS; i++) { + send_relative_move(fd_send, 0, 0); + usleep(1000); } - cleanup(); - tst_exit(); + verify_no_events_queued(fd_recv); } static void setup(void) { - tst_require_root(); + fd_send = open_uinput(); + if (fd_send == -1) + tst_brk(TCONF, "Virtual device is not available"); - fd = open_uinput(); - setup_mouse_events(fd); - create_device(fd); + setup_mouse_events(fd_send); + create_input_device(fd_send); - fd2 = open_device(); -} - -static void send_events(void) -{ - int nb; - - for (nb = 0; nb < NB_TEST; ++nb) { - send_rel_move(fd, 0, 0); - usleep(1000); - } + fd_recv = open_event_device(); } static void cleanup(void) { - if (fd2 > 0 && close(fd2)) - tst_resm(TWARN | TERRNO, "close(fd2)"); + if (fd_send != -1) + destroy_input_device(fd_send); - destroy_device(fd); + if (fd_recv != -1) + SAFE_CLOSE(fd_recv); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, +}; diff --git a/testcases/kernel/input/input05.c b/testcases/kernel/input/input05.c index 46b4fe8b..19cf8f1c 100755 --- a/testcases/kernel/input/input05.c +++ b/testcases/kernel/input/input05.c @@ -1,107 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda - * - * 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 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ - /* - * Create a virtual device (mouse), send events to /dev/uinput - * and Check that events not advertised in the input device bits - * are filtered. - */ +/*\ + * Verify that /dev/input/eventX doesn't receive any event sent from a virtual + * device, that in our case is a mouse, when events not advertised in the input + * device bits are filtered. + */ -#include #include -#include "test.h" -#include "safe_macros.h" -#include "lapi/fcntl.h" -#include "input_helper.h" +#include "input_common.h" -#define X_VALUE 10 -#define Y_VALUE 10 +#define NUM_EVENTS 20 +#define MOVE_X 10 +#define MOVE_Y 10 -#define NB_TEST 20 +static int fd_send = -1; +static int fd_recv = -1; -static void setup(void); -static void send_events(void); -static void cleanup(void); - -static int fd; -static int fd2; - -char *TCID = "input05"; - -int main(int ac, char **av) +static void run(void) { - int lc; - int pid; + tst_res(TINFO, "Sending relative mouse move (%i, %i)", MOVE_X, MOVE_Y); - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - pid = tst_fork(); - - switch (pid) { - case 0: - send_events(); - exit(0); - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); - default: - if (no_events_queued(fd2, 1)) - tst_resm(TPASS, "No data received in eventX"); - else - tst_resm(TFAIL, "Data received in eventX"); - break; - } - - SAFE_WAITPID(NULL, pid, NULL, 0); + for (int i = 0; i < NUM_EVENTS; i++) { + send_relative_move(fd_send, MOVE_X, MOVE_Y); + usleep(1000); } - cleanup(); - tst_exit(); + verify_no_events_queued(fd_recv); } static void setup(void) { - tst_require_root(); + fd_send = open_uinput(); + if (fd_send == -1) + tst_brk(TCONF, "Virtual device is not available"); - fd = open_uinput(); + SAFE_IOCTL(fd_send, UI_SET_EVBIT, EV_KEY); + SAFE_IOCTL(fd_send, UI_SET_KEYBIT, BTN_LEFT); + create_input_device(fd_send); - SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_KEY); - SAFE_IOCTL(NULL, fd, UI_SET_KEYBIT, BTN_LEFT); - - create_device(fd); - - fd2 = open_device(); -} - -static void send_events(void) -{ - int nb; - - for (nb = 0; nb < NB_TEST; ++nb) { - send_rel_move(fd, X_VALUE, Y_VALUE); - usleep(1000); - } + fd_recv = open_event_device(); } static void cleanup(void) { - destroy_device(fd); + if (fd_send != -1) + destroy_input_device(fd_send); + + if (fd_recv != -1) + SAFE_CLOSE(fd_recv); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, +}; diff --git a/testcases/kernel/input/input06.c b/testcases/kernel/input/input06.c index b698c277..969c90f6 100755 --- a/testcases/kernel/input/input06.c +++ b/testcases/kernel/input/input06.c @@ -1,102 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda - * - * 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 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ - /* - * Create a virtual device, activate auto-repeat and - * and check that auto repeat is working - */ +/*\ + * Verify that auto-repeat is working on a virtual device, that in our case + * it's a keyboard. + */ -#include #include -#include -#include "test.h" -#include "safe_macros.h" -#include "lapi/fcntl.h" -#include "input_helper.h" +#include "input_common.h" -static void setup(void); -static void send_events(void); -static int check_events(void); -static void cleanup(void); - -static int fd; -static int fd2; struct input_event events[64]; static int num_events; static int ev_iter; - -char *TCID = "input06"; - -int main(int ac, char **av) -{ - int lc; - int pid; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - pid = tst_fork(); - - switch (pid) { - case 0: - send_events(); - exit(0); - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); - default: - if (!check_events()) - tst_resm(TFAIL, - "Wrong data received in eventX"); - else - tst_resm(TPASS, "Data received in eventX"); - break; - } - - SAFE_WAITPID(NULL, pid, NULL, 0); - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_require_root(); - - fd = open_uinput(); - - SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_KEY); - SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_REP); - SAFE_IOCTL(NULL, fd, UI_SET_KEYBIT, KEY_X); - - create_device(fd); - - fd2 = open_device(); - SAFE_IOCTL(NULL, fd2, EVIOCGRAB, 1); -} +static int fd_send = -1; +static int fd_recv = -1; static void send_events(void) { - send_event(fd, EV_KEY, KEY_X, 1); - send_event(fd, EV_SYN, 0, 0); + send_event(fd_send, EV_KEY, KEY_X, 1); + send_event(fd_send, EV_SYN, 0, 0); /* * Sleep long enough to keep the key pressed for some time @@ -106,8 +32,8 @@ static void send_events(void) */ usleep(500000); - send_event(fd, EV_KEY, KEY_X, 0); - send_event(fd, EV_SYN, 0, 0); + send_event(fd_send, EV_KEY, KEY_X, 0); + send_event(fd_send, EV_SYN, 0, 0); } static int check_event(struct input_event *iev, int event, int code, int value) @@ -117,20 +43,18 @@ static int check_event(struct input_event *iev, int event, int code, int value) static void read_events(void) { - int rd = read(fd2, events, sizeof(events)); - if (rd < 0) - tst_brkm(TBROK | TERRNO, cleanup, "read() failed"); + int num_bytes = SAFE_READ(0, fd_recv, events, sizeof(events)); - if (rd == 0) - tst_brkm(TBROK, cleanup, "Failed to read events"); + if (!num_bytes) + tst_brk(TBROK, "Failed to read events"); - if (rd % sizeof(struct input_event) != 0) { - tst_brkm(TBROK, cleanup, "read size %i not multiple of %zu", - rd, sizeof(struct input_event)); + if (num_bytes % sizeof(struct input_event) != 0) { + tst_brk(TBROK, "Read size %i is not multiple of %zu", + num_bytes, sizeof(struct input_event)); } ev_iter = 0; - num_events = rd / sizeof(struct input_event); + num_events = num_bytes / sizeof(struct input_event); } static int have_events(void) @@ -146,29 +70,37 @@ static struct input_event *next_event(void) return &events[ev_iter++]; } +static int check_event_code(struct input_event *iev, int event, int code) +{ + return iev->type == event && iev->code == code; +} + static int parse_autorepeat_config(struct input_event *iev) { if (!check_event_code(iev, EV_REP, REP_DELAY)) { - tst_resm(TFAIL, - "Didn't get EV_REP configuration with code REP_DELAY"); + tst_res(TFAIL, "Didn't get EV_REP type with REP_DELAY code"); return 0; } if (!check_event_code(next_event(), EV_REP, REP_PERIOD)) { - tst_resm(TFAIL, - "Didn't get EV_REP configuration with code REP_PERIOD"); + tst_res(TFAIL, "Didn't get EV_REP type with REP_PERIOD code"); return 0; } return 1; } +static int check_sync_event(struct input_event *iev) +{ + return check_event_code(iev, EV_SYN, SYN_REPORT); +} + static int parse_key(struct input_event *iev) { int autorep_count = 0; if (!check_event(iev, EV_KEY, KEY_X, 1) || !check_sync_event(next_event())) { - tst_resm(TFAIL, "Didn't get expected key press for KEY_X"); + tst_res(TFAIL, "Didn't get expected key press for KEY_X"); return 0; } @@ -180,19 +112,16 @@ static int parse_key(struct input_event *iev) /* make sure we have at least one auto-repeated key event */ if (!autorep_count) { - tst_resm(TFAIL, - "Didn't get autorepeat events for the key - KEY_X"); + tst_res(TFAIL, "Didn't get autorepeat events for the key - KEY_X"); return 0; } if (!check_event(iev, EV_KEY, KEY_X, 0) || !check_sync_event(next_event())) { - tst_resm(TFAIL, - "Didn't get expected key release for KEY_X"); + tst_res(TFAIL, "Didn't get expected key release for KEY_X"); return 0; } - tst_resm(TINFO, - "Received %d repititions for KEY_X", autorep_count); + tst_res(TINFO, "Received %d repetitions for KEY_X", autorep_count); return 1; } @@ -218,8 +147,7 @@ static int check_events(void) rep_keys_done = 1; break; default: - tst_resm(TFAIL, - "Unexpected event type '0x%04x' received", + tst_res(TFAIL, "Unexpected event type '0x%04x' received", iev->type); ret = 0; break; @@ -232,10 +160,48 @@ static int check_events(void) return ret; } +static void run(void) +{ + if (!SAFE_FORK()) { + send_events(); + exit(0); + } + + if (!check_events()) + tst_res(TFAIL, "Wrong data received from input device"); + else + tst_res(TPASS, "Data received from input device"); +} + +static void setup(void) +{ + fd_send = open_uinput(); + if (fd_send == -1) + tst_brk(TCONF, "Virtual device is not available"); + + SAFE_IOCTL(fd_send, UI_SET_EVBIT, EV_KEY); + SAFE_IOCTL(fd_send, UI_SET_EVBIT, EV_REP); + SAFE_IOCTL(fd_send, UI_SET_KEYBIT, KEY_X); + + create_input_device(fd_send); + + fd_recv = open_event_device(); + SAFE_IOCTL(fd_recv, EVIOCGRAB, 1); +} + static void cleanup(void) { - if (fd2 > 0 && close(fd2)) - tst_resm(TWARN | TERRNO, "close(fd2) failed"); + if (fd_send != -1) + destroy_input_device(fd_send); - destroy_device(fd); + if (fd_recv != -1) + SAFE_CLOSE(fd_recv); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .forks_child = 1, +}; diff --git a/testcases/kernel/input/input_common.h b/testcases/kernel/input/input_common.h new file mode 100644 index 00000000..5b175577 --- /dev/null +++ b/testcases/kernel/input/input_common.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef INPUT_COMMON_H__ +#define INPUT_COMMON_H__ + +#include +#include + +#include "tst_test.h" +#include "tst_uinput.h" + +static inline int open_event_device(void) +{ + int fd; + char path[1024]; + char *device; + char *handlers; + + memset(path, 0, sizeof(path)); + + handlers = get_input_field_value('H'); + device = strtok(handlers, " "); + + while (device) { + if (strstr(device, "event") != NULL) { + memset(path, 0, sizeof(path)); + snprintf(path, sizeof(path), "/dev/input/%s", device); + + if (!TST_RETRY_FUNC(access(path, F_OK), TST_RETVAL_EQ0)) { + tst_res(TINFO, "Found event device: %s", path); + break; + } + } + + device = strtok(NULL, " "); + } + + free(handlers); + + if (path[0] == '\0') + tst_brk(TBROK, "Can't find event device"); + + fd = SAFE_OPEN(path, O_RDONLY); + + return fd; +} + +static inline void send_event( + const int fd, const int event, + const int code, const int value) +{ + struct input_event ev = { + .type = event, + .code = code, + .value = value, + }; + + SAFE_WRITE(SAFE_WRITE_ALL, fd, &ev, sizeof(ev)); +} + +static inline void send_relative_move(const int fd, const int x, const int y) +{ + send_event(fd, EV_REL, REL_X, x); + send_event(fd, EV_REL, REL_Y, y); + send_event(fd, EV_SYN, 0, 0); +} + +static inline void verify_no_events_queued(const int fd_recv) +{ + int num_bytes; + int num_events; + struct input_event ev; + struct pollfd fds = { + .fd = fd_recv, + .events = POLLIN + }; + + num_events = poll(&fds, 1, 30); + + TST_EXP_EQ_LI(num_events, 0); + if (!num_events) + return; + + num_bytes = SAFE_READ(0, fd_recv, &ev, sizeof(ev)); + if (!num_bytes) + return; + + tst_res(TFAIL, "Received unexpected event: " + "type=%i, code=%i, value=%i", + ev.type, + ev.code, + ev.value); +} +#endif diff --git a/testcases/kernel/input/input_helper.c b/testcases/kernel/input/input_helper.c deleted file mode 100755 index 09530fb4..00000000 --- a/testcases/kernel/input/input_helper.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2015 Cedric Hnyda - * - * 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 - */ - -#include -#include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" -#include "input_helper.h" - -#define VIRTUAL_DEVICE "virtual-device-ltp" - -#define VIRTUAL_DEVICE_REGEX "*virtual-device-ltp*" - -static int uinput_loaded; -static int check_device(void); - -static int try_open_device(void) -{ - char path[256]; - char name[256]; - int ret, fd = -1; - unsigned int i; - - for (i = 0; i < 100; i++) { - snprintf(path, sizeof(path), "/dev/input/event%i", i); - - fd = open(path, O_RDONLY); - - if (fd < 0 && errno == ENOENT) - continue; - - if (fd < 0) { - tst_resm(TINFO | TERRNO, "failed to open %s", path); - break; - } - - ret = ioctl(fd, EVIOCGNAME(sizeof(name)), name); - if (ret < 0) { - tst_resm(TINFO | TERRNO, - "ioctl(%s, EVIOCGNAME(256), ...) failed", - path); - break; - } - - if (strcmp(name, VIRTUAL_DEVICE) == 0) - return fd; - close(fd); - } - - return -1; -} - -int open_device(void) -{ - int fd; - int retries = 10; - - while (retries--) { - fd = try_open_device(); - if (fd > 0) - return fd; - tst_resm(TINFO, "Device not found, retrying..."); - usleep(10000); - } - - tst_brkm(TBROK, NULL, "Unable to find the input device"); -} - -static int try_load_uinput(void) -{ - const char *argv[] = {"modprobe", "uinput", NULL}; - int ret; - - tst_resm(TINFO, "Trying to load uinput kernel module"); - - ret = tst_cmd(NULL, argv, NULL, NULL, TST_CMD_PASS_RETVAL); - if (ret) { - tst_resm(TINFO, "Failed to load the uinput module"); - return 0; - } - - return 1; -} - -static void unload_uinput(void) -{ - const char *argv[] = {"modprobe", "-r", "uinput", NULL}; - int ret; - - tst_resm(TINFO, "Unloading uinput kernel module"); - - ret = tst_cmd(NULL, argv, NULL, NULL, TST_CMD_PASS_RETVAL); - if (ret) - tst_resm(TWARN, "Failed to unload uinput module"); -} - -static const char *uinput_paths[] = { - "/dev/input/uinput", - "/dev/uinput", -}; - -static int try_open_uinput(void) -{ - unsigned int i; - int fd; - - for (i = 0; i < ARRAY_SIZE(uinput_paths); i++) { - fd = open(uinput_paths[i], O_WRONLY | O_NONBLOCK); - - if (fd > 0) { - tst_resm(TINFO, "Found uinput dev at %s", - uinput_paths[i]); - return fd; - } - - if (fd < 0 && errno != ENOENT) { - tst_brkm(TBROK | TERRNO, NULL, - "open(%s)", uinput_paths[i]); - } - } - - return -1; -} - -int open_uinput(void) -{ - int fd; - int retries = 10; - - fd = try_open_uinput(); - if (fd > 0) - return fd; - - if (try_load_uinput()) { - while (retries--) { - fd = try_open_uinput(); - if (fd > 0) { - uinput_loaded = 1; - return fd; - } - tst_resm(TINFO, "Uinput dev not found, retrying..."); - usleep(10000); - } - - unload_uinput(); - } - - tst_brkm(TCONF, NULL, "Unable to find and open uinput"); -} - -void send_event(int fd, int event, int code, int value) -{ - struct input_event ev = { - .type = event, - .code = code, - .value = value, - }; - - SAFE_WRITE(NULL, SAFE_WRITE_ALL, fd, &ev, sizeof(ev)); -} - -void send_rel_move(int fd, int x, int y) -{ - send_event(fd, EV_REL, REL_X, x); - send_event(fd, EV_REL, REL_Y, y); - send_event(fd, EV_SYN, 0, 0); -} - -void create_device(int fd) -{ - int nb; - struct uinput_user_dev uidev = { - .name = VIRTUAL_DEVICE, - .id = { - .bustype = BUS_USB, - .vendor = 0x1, - .product = 0x1, - .version = 1, - } - }; - - SAFE_WRITE(NULL, SAFE_WRITE_ALL, fd, &uidev, sizeof(uidev)); - SAFE_IOCTL(NULL, fd, UI_DEV_CREATE, NULL); - - for (nb = 100; nb > 0; nb--) { - if (check_device()) - return; - usleep(10000); - } - - destroy_device(fd); - tst_brkm(TBROK, NULL, "Failed to create device"); -} - -void setup_mouse_events(int fd) -{ - SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_KEY); - SAFE_IOCTL(NULL, fd, UI_SET_KEYBIT, BTN_LEFT); - SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_REL); - SAFE_IOCTL(NULL, fd, UI_SET_RELBIT, REL_X); - SAFE_IOCTL(NULL, fd, UI_SET_RELBIT, REL_Y); -} - -void destroy_device(int fd) -{ - SAFE_IOCTL(NULL, fd, UI_DEV_DESTROY, NULL); - SAFE_CLOSE(NULL, fd); - - if (uinput_loaded) - unload_uinput(); -} - -int check_event_code(struct input_event *iev, int event, int code) -{ - return iev->type == event && iev->code == code; -} - -int check_sync_event(struct input_event *iev) -{ - return check_event_code(iev, EV_SYN, SYN_REPORT); -} - -/* - * the value of stray_sync_event: - * 0: EV_SYN/SYN_REPORT events should not be received in /dev/input/eventX - * 1: EV_SYN/SYN_REPORT events may be received in /dev/input/eventX - * On an old kernel(before v3.7.0), EV_SYN/SYN_REPORT events are always - * received even though we send empty moves. - */ -int no_events_queued(int fd, int stray_sync_event) -{ - struct pollfd fds = {.fd = fd, .events = POLLIN}; - int ret, res; - struct input_event ev; - - ret = poll(&fds, 1, 30); - - if (ret > 0) { - res = read(fd, &ev, sizeof(ev)); - - if (res == sizeof(ev)) { - tst_resm(TINFO, - "Unexpected ev type=%i code=%i value=%i", - ev.type, ev.code, ev.value); - } - } - - return ret == 0; -} - -static int check_device(void) -{ - FILE *file; - char line[256]; - - file = fopen("/proc/bus/input/devices", "r"); - if (!file) - return 0; - - while (fgets(line, 256, file)) { - if (fnmatch(VIRTUAL_DEVICE_REGEX, line, 0) == 0) - return 1; - } - - fclose(file); - - return 0; -} diff --git a/testcases/kernel/input/input_helper.h b/testcases/kernel/input/input_helper.h deleted file mode 100755 index 7f61be1e..00000000 --- a/testcases/kernel/input/input_helper.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2015 Cedric Hnyda - * - * 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 - */ - -#ifndef INPUT_HELPER_H -#define INPUT_HELPER_H - -#include -#include - -int open_device(void); -void send_rel_move(int fd, int x, int y); -void send_event(int fd, int event, int code, int value); -int open_uinput(void); -void create_device(int fd); -void setup_mouse_events(int fd); -void destroy_device(int fd); -int check_event_code(struct input_event *iev, int event, int code); -int check_sync_event(struct input_event *iev); -int no_events_queued(int fd, int stray_sync_event); - -#endif /* INPUT_HELPER_H */ diff --git a/testcases/kernel/io/direct_io/diotest4.c b/testcases/kernel/io/direct_io/diotest4.c index 45c677b5..ad00fa3e 100755 --- a/testcases/kernel/io/direct_io/diotest4.c +++ b/testcases/kernel/io/direct_io/diotest4.c @@ -270,6 +270,7 @@ int main(int argc, char *argv[]) case TST_NFS_MAGIC: case TST_BTRFS_MAGIC: case TST_FUSE_MAGIC: + case TST_TMPFS_MAGIC: tst_resm(TCONF, "%s supports odd count IO", tst_fs_type_name(fs_type)); break; @@ -443,6 +444,7 @@ int main(int argc, char *argv[]) case TST_NFS_MAGIC: case TST_BTRFS_MAGIC: case TST_FUSE_MAGIC: + case TST_TMPFS_MAGIC: tst_resm(TCONF, "%s supports non-aligned buffer", tst_fs_type_name(fs_type)); break; diff --git a/testcases/kernel/io/direct_io/diotest_routines.c b/testcases/kernel/io/direct_io/diotest_routines.c index fe03630e..793572c3 100755 --- a/testcases/kernel/io/direct_io/diotest_routines.c +++ b/testcases/kernel/io/direct_io/diotest_routines.c @@ -55,11 +55,7 @@ */ void fillbuf(char *buf, int count, char value) { - while (count > 0) { - strncpy(buf, &value, 1); - buf++; - count = count - 1; - } + memset(buf, value, count); } void vfillbuf(struct iovec *iv, int vcnt, char value) diff --git a/testcases/kernel/io/ltp-aiodio/aio-stress.c b/testcases/kernel/io/ltp-aiodio/aio-stress.c index 5c3a0a3a..5cce92df 100755 --- a/testcases/kernel/io/ltp-aiodio/aio-stress.c +++ b/testcases/kernel/io/ltp-aiodio/aio-stress.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2004 SuSE, Inc. All Rights Reserved. * Written by: Chris Mason @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test creates a series of files and start AIO operations on them. * AIO is done in a rotating loop: first file1.bin gets 8 requests, then * file2.bin, then file3.bin etc. As each file finishes writing, test switches @@ -916,14 +914,15 @@ static void setup_ious(struct thread_info *t, int num_files, int depth, int recl { int i; size_t bytes = num_files * depth * sizeof(*t->ios); + char *buffer = aligned_buffer; t->ios = SAFE_MALLOC(bytes); memset(t->ios, 0, bytes); for (i = 0; i < depth * num_files; i++) { - t->ios[i].buf = aligned_buffer; - aligned_buffer += padded_reclen; + t->ios[i].buf = buffer; + buffer += padded_reclen; t->ios[i].buf_size = reclen; if (verify) memset(t->ios[i].buf, 'b', reclen); @@ -934,7 +933,7 @@ static void setup_ious(struct thread_info *t, int num_files, int depth, int recl } if (verify) { - verify_buf = aligned_buffer; + verify_buf = buffer; memset(verify_buf, 'b', reclen); } @@ -1122,7 +1121,12 @@ restart: while (t->finished_opers) { oper = t->finished_opers; oper_list_del(oper, &t->finished_opers); - status = finish_oper(t, oper); + status = finish_oper(t, oper) ? : status; + } + while (t->active_opers) { + oper = t->active_opers; + oper_list_del(oper, &t->active_opers); + status = finish_oper(t, oper) ? : status; } if (t->num_global_pending) @@ -1225,19 +1229,6 @@ static void setup(void) tst_brk(TBROK, "Invalid shm option '%s'", str_use_shm); } } -} - -static void run(void) -{ - char files[num_files][265]; - int first_stage = WRITE; - struct io_oper *oper; - int status = 0; - int open_fds = 0; - struct thread_info *t; - int rwfd; - int i; - int j; /* * make sure we don't try to submit more I/O than we have allocated @@ -1253,6 +1244,22 @@ static void run(void) tst_res(TINFO, "Dropping thread count to the number of contexts %d", num_threads); } + if (setup_shared_mem(num_threads, num_files * num_contexts, depth, rec_len)) + tst_brk(TBROK, "error in setup_shared_mem"); +} + +static void run(void) +{ + char files[num_files][265]; + int first_stage = WRITE; + struct io_oper *oper; + int status = 0; + int open_fds = 0; + struct thread_info *t; + int rwfd; + int i; + int j; + t = SAFE_MALLOC(num_threads * sizeof(*t)); memset(t, 0, num_threads * sizeof(*t)); global_thread_info = t; @@ -1319,8 +1326,6 @@ static void run(void) } } - if (setup_shared_mem(num_threads, num_files * num_contexts, depth, rec_len)) - tst_brk(TBROK, "error in setup_shared_mem"); for (i = 0; i < num_threads; i++) setup_ious(&t[i], t[i].num_files, depth, rec_len, max_io_submit); @@ -1336,6 +1341,13 @@ static void run(void) for (i = 0; i < num_files; i++) SAFE_UNLINK(files[i]); + for (i = 0; i < num_threads; i++) { + free(t[i].ios); + free(t[i].iocbs); + free(t[i].events); + } + free(t); + if (status) tst_res(TFAIL, "Test did not pass"); else @@ -1347,7 +1359,7 @@ static struct tst_test test = { .setup = setup, .needs_tmpdir = 1, .needs_root = 1, - .max_runtime = 1800, + .timeout = 1800, .options = (struct tst_option[]){ { "a:", &str_iterations, "Total number of ayncs I/O the program will run (default 500)" }, { "b:", &str_max_io_submit, "Max number of iocbs to give io_submit at once" }, diff --git a/testcases/kernel/io/ltp-aiodio/aiocp.c b/testcases/kernel/io/ltp-aiodio/aiocp.c index 6212d8ee..2a4afe11 100755 --- a/testcases/kernel/io/ltp-aiodio/aiocp.c +++ b/testcases/kernel/io/ltp-aiodio/aiocp.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Copy file by using an async I/O state machine. * * - Start read request @@ -322,7 +320,7 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .needs_tmpdir = 1, - .max_runtime = 1800, + .runtime = 1800, .needs_root = 1, .options = (struct tst_option[]) { {"b:", &str_aio_blksize, "Size of writing blocks (default 1K)"}, diff --git a/testcases/kernel/io/ltp-aiodio/aiodio_append.c b/testcases/kernel/io/ltp-aiodio/aiodio_append.c index 45e96879..f1e7025f 100755 --- a/testcases/kernel/io/ltp-aiodio/aiodio_append.c +++ b/testcases/kernel/io/ltp-aiodio/aiodio_append.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Append zeroed data to a file using libaio while other processes are doing * buffered reads and check if the buffer reads always see zero. */ @@ -190,7 +188,7 @@ static struct tst_test test = { .cleanup = cleanup, .needs_tmpdir = 1, .forks_child = 1, - .max_runtime = 1800, + .runtime = 1800, .options = (struct tst_option[]) { {"n:", &str_numchildren, "Number of threads (default 16)"}, {"s:", &str_writesize, "Size of the file to write (default 64K)"}, diff --git a/testcases/kernel/io/ltp-aiodio/aiodio_sparse.c b/testcases/kernel/io/ltp-aiodio/aiodio_sparse.c index 595c7622..c4878f27 100755 --- a/testcases/kernel/io/ltp-aiodio/aiodio_sparse.c +++ b/testcases/kernel/io/ltp-aiodio/aiodio_sparse.c @@ -10,8 +10,6 @@ */ /*\ - * [Description] - * * Create a sparse file and write zeroes to it using libaio while other * processes are doing buffered reads and check if the buffer reads always see * zero. @@ -244,7 +242,7 @@ static struct tst_test test = { "tmpfs", NULL }, - .max_runtime = 1800, + .runtime = 1800, }; #else TST_TEST_TCONF("test requires libaio and its development packages"); diff --git a/testcases/kernel/io/ltp-aiodio/common.h b/testcases/kernel/io/ltp-aiodio/common.h index 200bbe18..9a2d2716 100755 --- a/testcases/kernel/io/ltp-aiodio/common.h +++ b/testcases/kernel/io/ltp-aiodio/common.h @@ -62,8 +62,12 @@ static inline void io_read(const char *filename, int filesize, volatile int *run int i; int r; - while ((fd = open(filename, O_RDONLY, 0666)) < 0) + while ((fd = open(filename, O_RDONLY, 0666)) < 0) { + if (!*run_child) + return; + usleep(100); + } tst_res(TINFO, "child %i reading file", getpid()); @@ -102,8 +106,12 @@ static inline void io_read_eof(const char *filename, volatile int *run_child) int fd; int r; - while ((fd = open(filename, O_RDONLY, 0666)) < 0) + while ((fd = open(filename, O_RDONLY, 0666)) < 0) { + if (!*run_child) + return; + usleep(100); + } tst_res(TINFO, "child %i reading file", getpid()); diff --git a/testcases/kernel/io/ltp-aiodio/dio_append.c b/testcases/kernel/io/ltp-aiodio/dio_append.c index 057ae73d..1febafb8 100755 --- a/testcases/kernel/io/ltp-aiodio/dio_append.c +++ b/testcases/kernel/io/ltp-aiodio/dio_append.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Appends zeroed data to a file using O_DIRECT while a child processes are * doing buffered reads after seeking to the end of the file and checks if the * buffer reads always see zero. @@ -33,7 +31,7 @@ static void setup(void) { numchildren = 16; writesize = 64 * 1024; - appends = 1000; + appends = 10000; if (tst_parse_int(str_numchildren, &numchildren, 1, INT_MAX)) tst_brk(TBROK, "Invalid number of children '%s'", str_numchildren); @@ -44,6 +42,9 @@ static void setup(void) if (tst_parse_int(str_appends, &appends, 1, INT_MAX)) tst_brk(TBROK, "Invalid number of appends '%s'", str_appends); + if (!tst_fs_has_free(".", appends, writesize)) + tst_brk(TCONF, "Not enough space to run the test"); + run_child = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); } @@ -93,11 +94,11 @@ static struct tst_test test = { .cleanup = cleanup, .needs_tmpdir = 1, .forks_child = 1, - .max_runtime = 1800, + .runtime = 1800, .options = (struct tst_option[]) { {"n:", &str_numchildren, "Number of processes (default 16)"}, {"w:", &str_writesize, "Write size for each append (default 64K)"}, - {"c:", &str_appends, "Number of appends (default 1000)"}, + {"c:", &str_appends, "Number of appends (default 10000)"}, {} }, .skip_filesystems = (const char *[]) { diff --git a/testcases/kernel/io/ltp-aiodio/dio_read.c b/testcases/kernel/io/ltp-aiodio/dio_read.c index 54a0bc5c..1c913cc2 100755 --- a/testcases/kernel/io/ltp-aiodio/dio_read.c +++ b/testcases/kernel/io/ltp-aiodio/dio_read.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Create a file using buffered writes while other processes are doing * O_DIRECT reads and check if the buffer reads always see zero. */ @@ -30,7 +28,7 @@ static int numchildren = 8; static long long writesize = 32 * 1024 * 1024; static long long readsize = 32 * 1024 * 1024; static long long filesize = 128 * 1024 * 1024; -static int *children_completed; +static tst_atomic_t *children_completed; static char *iobuf; static int fd; @@ -178,7 +176,7 @@ static struct tst_test test = { .cleanup = cleanup, .needs_tmpdir = 1, .forks_child = 1, - .max_runtime = 1800, + .runtime = 1800, .options = (struct tst_option[]) { {"n:", &str_numchildren, "Number of threads (default 8)"}, {"w:", &str_writesize, "Size of writing blocks (default 32M)"}, diff --git a/testcases/kernel/io/ltp-aiodio/dio_sparse.c b/testcases/kernel/io/ltp-aiodio/dio_sparse.c index 04b93ff2..c87e5ab1 100755 --- a/testcases/kernel/io/ltp-aiodio/dio_sparse.c +++ b/testcases/kernel/io/ltp-aiodio/dio_sparse.c @@ -11,8 +11,6 @@ */ /*\ - * [Description] - * * Create a sparse file using O_DIRECT while other processes are doing * buffered reads and check if the buffer reads always see zero. */ @@ -135,5 +133,5 @@ static struct tst_test test = { "tmpfs", NULL }, - .max_runtime = 1800, + .runtime = 1800, }; diff --git a/testcases/kernel/io/ltp-aiodio/dio_truncate.c b/testcases/kernel/io/ltp-aiodio/dio_truncate.c index 2c54f898..d4e7d9ea 100755 --- a/testcases/kernel/io/ltp-aiodio/dio_truncate.c +++ b/testcases/kernel/io/ltp-aiodio/dio_truncate.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test is mixing direct I/O and truncate operations checking if they can * be used together at the same time. Multiple children are spawned to read a * file that is written to using direct I/O and truncated in a loop. @@ -170,7 +168,7 @@ static struct tst_test test = { .cleanup = cleanup, .needs_tmpdir = 1, .forks_child = 1, - .max_runtime = 1800, + .runtime = 1800, .options = (struct tst_option[]) { {"n:", &str_numchildren, "Number of threads (default 16)"}, {"s:", &str_filesize, "Size of file (default 64K)"}, diff --git a/testcases/kernel/irq/irqbalance01.c b/testcases/kernel/irq/irqbalance01.c index a3d29aec..664fced1 100755 --- a/testcases/kernel/irq/irqbalance01.c +++ b/testcases/kernel/irq/irqbalance01.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) 2021 SUSE LLC */ /*\ - * [Description] - * * Check that something (e.g. irqbalance daemon) is performing IRQ * load balancing. * @@ -93,7 +91,7 @@ static void collect_irq_info(void) char path[PATH_MAX]; size_t row, col, len; long acc; - unsigned int cpu_total, bit; + unsigned int cpu_total, bit, row_parsed; nr_cpus = 0; nr_irqs = 0; @@ -136,7 +134,7 @@ static void collect_irq_info(void) c = first_row; acc = -1; - row = col = 0; + row = col = row_parsed = 0; /* Parse columns containing IRQ counts and IRQ IDs into acc. Ignore * everything else. */ @@ -154,7 +152,9 @@ static void collect_irq_info(void) if (acc != -1) tst_brk(TBROK, "Unexpected EOL"); col = 0; - row++; + if (row_parsed) + row++; + row_parsed = 0; break; case '0' ... '9': if (acc == -1) @@ -168,6 +168,7 @@ static void collect_irq_info(void) tst_brk(TBROK, "Unexpected ':'"); irq_ids[row] = acc; acc = -1; + row_parsed = 1; break; default: acc = -1; @@ -284,9 +285,18 @@ static void evidence_of_change(void) } } - tst_res(changed ? TPASS : TFAIL, - "Heuristic: Detected %zu irq-cpu pairs have been dissallowed", - changed); + if (changed) { + tst_res(TPASS, "IRQs assignments have changed %zu times", + changed); + } else { + + tst_res(TFAIL, "IRQ balancing has not been detected"); + + tst_printf("Please, check that:\n" + "- balancing service is not running\n" + "- balancing service is running but rules didn't change\n" + "- balancing rules have been changed, but CPUs didn't perform any interrupt"); + } } static void setup(void) diff --git a/testcases/kernel/kvm/.gitignore b/testcases/kernel/kvm/.gitignore index 9638a6fc..f6935c17 100644 --- a/testcases/kernel/kvm/.gitignore +++ b/testcases/kernel/kvm/.gitignore @@ -2,3 +2,6 @@ /kvm_svm01 /kvm_svm02 /kvm_svm03 +/kvm_svm04 +/kvm_vmx01 +/kvm_vmx02 diff --git a/testcases/kernel/kvm/Makefile b/testcases/kernel/kvm/Makefile index ce4a5ede..e93528a3 100644 --- a/testcases/kernel/kvm/Makefile +++ b/testcases/kernel/kvm/Makefile @@ -24,6 +24,10 @@ endif ifeq ($(HOST_CPU),x86) GUEST_CFLAGS += -m32 ASFLAGS += --32 + + ifdef LTP_CFLAGS_FFIXED_EBP + GUEST_CFLAGS += -ffixed-ebp + endif endif # Some distros enable -pie by default. That breaks KVM payload linking. @@ -56,11 +60,11 @@ include $(top_srcdir)/include/mk/generic_leaf_target.mk %-payload.o: %.c lib_guest.o $(ARCH_OBJ) ifdef VERBOSE $(CC) $(GUEST_CPPFLAGS) $(GUEST_CFLAGS) $(GUEST_LDFLAGS) -o $*-payload.elf $^ $(GUEST_LDLIBS) - objcopy -O binary -j .init.boot -j .text -j .data -j .init -j .preinit_array -j .init_array --gap-fill=0 $*-payload.elf $*-payload.bin + $(OBJCOPY) -O binary -j .init.boot -j .text -j .data -j .init -j .preinit_array -j .init_array --gap-fill=0 $*-payload.elf $*-payload.bin $(KVM_LD) -z noexecstack -r -T $(abs_srcdir)/linker/payload.lds --oformat=$(BIN_FORMAT) -o $@ $*-payload.bin else @$(CC) $(GUEST_CPPFLAGS) $(GUEST_CFLAGS) $(GUEST_LDFLAGS) -o $*-payload.elf $^ $(GUEST_LDLIBS) - @objcopy -O binary -j .init.boot -j .text -j .data -j .init -j .preinit_array -j .init_array --gap-fill=0 $*-payload.elf $*-payload.bin + @$(OBJCOPY) -O binary -j .init.boot -j .text -j .data -j .init -j .preinit_array -j .init_array --gap-fill=0 $*-payload.elf $*-payload.bin @$(KVM_LD) -z noexecstack -r -T $(abs_srcdir)/linker/payload.lds --oformat=$(BIN_FORMAT) -o $@ $*-payload.bin @echo KVM_CC $(target_rel_dir)$@ endif diff --git a/testcases/kernel/kvm/bootstrap_x86.S b/testcases/kernel/kvm/bootstrap_x86.S index a39c6bea..f19a9ea5 100644 --- a/testcases/kernel/kvm/bootstrap_x86.S +++ b/testcases/kernel/kvm/bootstrap_x86.S @@ -11,6 +11,9 @@ .set MSR_VM_HSAVE_PA, 0xc0010117 +.set VMX_VMCS_HOST_RSP, 0x6c14 +.set VMX_VMCS_HOST_RIP, 0x6c16 + /* * This section will be allocated at address 0x1000 and * jumped to from the reset stub provided by kvm_run. @@ -215,6 +218,8 @@ kvm_read_sregs: movw %ax, 8(%edi) mov %ss, %ax movw %ax, 10(%edi) + str %ax + movw %ax, 12(%edi) pop %edi ret @@ -359,6 +364,34 @@ kvm_svm_guest_entry: 1: hlt jmp 1b +/* vcpu structure address must be in %rdi */ +.macro load_vcpu_regs + movl 0x04(%edi), %eax + movl 0x0c(%edi), %ebx + movl 0x14(%edi), %ecx + movl 0x1c(%edi), %edx + /* save %edi last */ + movl 0x2c(%edi), %esi + movl 0x34(%edi), %ebp + /* skip %esp */ + movl 0x24(%edi), %edi +.endm + +/* vcpu structure address must be on top of the stack */ +.macro save_vcpu_regs + push %edi + movl 4(%esp), %edi + movl %eax, 0x04(%edi) + movl %ebx, 0x0c(%edi) + movl %ecx, 0x14(%edi) + movl %edx, 0x1c(%edi) + pop %eax + movl %eax, 0x24(%edi) + movl %esi, 0x2c(%edi) + movl %ebp, 0x34(%edi) + /* skip %esp */ +.endm + .global kvm_svm_vmrun kvm_svm_vmrun: push %edi @@ -375,17 +408,11 @@ kvm_svm_vmrun: vmsave push %eax - /* Load guest registers */ push %edi - movl (%edi), %eax - /* %eax is loaded by vmrun from VMCB */ - movl 0x0c(%edi), %ebx - movl 0x14(%edi), %ecx - movl 0x1c(%edi), %edx - movl 0x2c(%edi), %esi - movl 0x34(%edi), %ebp - /* %esp is loaded by vmrun from VMCB */ - movl 0x24(%edi), %edi + load_vcpu_regs + /* %eax = vcpu->vmcb; */ + movl (%esp), %eax + movl (%eax), %eax vmload vmrun @@ -393,8 +420,9 @@ kvm_svm_vmrun: /* Clear guest register buffer */ push %edi + push %eax push %ecx - movl 8(%esp), %edi + movl 12(%esp), %edi addl $4, %edi xorl %eax, %eax mov $32, %ecx @@ -402,17 +430,13 @@ kvm_svm_vmrun: cld rep stosl popfl - - /* Save guest registers */ pop %ecx pop %eax pop %edi - movl %ebx, 0x0c(%edi) - movl %ecx, 0x14(%edi) - movl %edx, 0x1c(%edi) - movl %eax, 0x24(%edi) - movl %esi, 0x2c(%edi) - movl %ebp, 0x34(%edi) + + save_vcpu_regs + pop %edi + /* Copy %eax and %esp from VMCB */ movl (%edi), %esi movl 0x5f8(%esi), %eax @@ -430,6 +454,67 @@ kvm_svm_vmrun: pop %edi ret +.global kvm_vmx_vmlaunch +kvm_vmx_vmlaunch: + push %edi + mov 8(%esp), %edi + push %ebx + push %esi + push %ebp + push %edi + + mov $VMX_VMCS_HOST_RSP, %eax + vmwrite %esp, %eax + jna vmx_vmwrite_error + mov $VMX_VMCS_HOST_RIP, %eax + lea vmx_vm_exit, %ebx + vmwrite %ebx, %eax + jna vmx_vmwrite_error + + load_vcpu_regs + vmlaunch + jmp vmx_vm_exit + +.global kvm_vmx_vmresume +kvm_vmx_vmresume: + push %edi + mov 8(%esp), %edi + push %ebx + push %esi + push %ebp + push %edi + + mov $VMX_VMCS_HOST_RSP, %eax + vmwrite %esp, %eax + jna vmx_vmwrite_error + mov $VMX_VMCS_HOST_RIP, %eax + lea vmx_vm_exit, %ebx + vmwrite %ebx, %eax + jna vmx_vmwrite_error + + load_vcpu_regs + vmresume + +vmx_vm_exit: + jna vmx_vmentry_error + save_vcpu_regs + xorl %eax, %eax + +vmx_vm_ret: + pop %edi + pop %ebp + pop %esi + pop %ebx + pop %edi + ret + +vmx_vmwrite_error: + movl $2, %eax + jmp vmx_vm_ret + +vmx_vmentry_error: + movl $1, %eax + jmp vmx_vm_ret .section .bss.pgtables, "aw", @nobits .global kvm_pagetable diff --git a/testcases/kernel/kvm/bootstrap_x86_64.S b/testcases/kernel/kvm/bootstrap_x86_64.S index b02dd4d9..d4b50128 100644 --- a/testcases/kernel/kvm/bootstrap_x86_64.S +++ b/testcases/kernel/kvm/bootstrap_x86_64.S @@ -12,6 +12,9 @@ .set MSR_VM_HSAVE_PA, 0xc0010117 +.set VMX_VMCS_HOST_RSP, 0x6c14 +.set VMX_VMCS_HOST_RIP, 0x6c16 + /* * This section will be allocated at address 0x1000 and * jumped to from the reset stub provided by kvm_run. @@ -319,6 +322,8 @@ kvm_read_sregs: movw %ax, 8(%rdi) mov %ss, %ax movw %ax, 10(%rdi) + str %ax + movw %ax, 12(%rdi) retq handle_interrupt: @@ -482,15 +487,71 @@ kvm_svm_guest_entry: 1: hlt jmp 1b -.global kvm_svm_vmrun -kvm_svm_vmrun: +/* vcpu structure address must be in %rdi */ +.macro load_vcpu_regs + movq 0x08(%rdi), %rax + movq 0x10(%rdi), %rbx + movq 0x18(%rdi), %rcx + movq 0x20(%rdi), %rdx + /* load %rdi last */ + movq 0x30(%rdi), %rsi + movq 0x38(%rdi), %rbp + /* skip %rsp */ + movq 0x48(%rdi), %r8 + movq 0x50(%rdi), %r9 + movq 0x58(%rdi), %r10 + movq 0x60(%rdi), %r11 + movq 0x68(%rdi), %r12 + movq 0x70(%rdi), %r13 + movq 0x78(%rdi), %r14 + movq 0x80(%rdi), %r15 + movq 0x28(%rdi), %rdi +.endm + +/* vcpu structure address must be on top of the stack */ +.macro save_vcpu_regs + pushq %rdi + movq 8(%rsp), %rdi + movq %rax, 0x08(%rdi) + movq %rbx, 0x10(%rdi) + movq %rcx, 0x18(%rdi) + movq %rdx, 0x20(%rdi) + popq %rax + movq %rax, 0x28(%rdi) + movq %rsi, 0x30(%rdi) + movq %rbp, 0x38(%rdi) + /* skip %rsp */ + movq %r8, 0x48(%rdi) + movq %r9, 0x50(%rdi) + movq %r10, 0x58(%rdi) + movq %r11, 0x60(%rdi) + movq %r12, 0x68(%rdi) + movq %r13, 0x70(%rdi) + movq %r14, 0x78(%rdi) + movq %r15, 0x80(%rdi) +.endm + +.macro push_local pushq %rbx pushq %rbp pushq %r12 pushq %r13 pushq %r14 pushq %r15 +.endm +.macro pop_local + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx +.endm + +.global kvm_svm_vmrun +kvm_svm_vmrun: + push_local clgi /* Save full host state */ @@ -501,48 +562,19 @@ kvm_svm_vmrun: vmsave pushq %rax - /* Load guest registers */ pushq %rdi - movq (%rdi), %rax - /* %rax is loaded by vmrun from VMCB */ - movq 0x10(%rdi), %rbx - movq 0x18(%rdi), %rcx - movq 0x20(%rdi), %rdx - movq 0x30(%rdi), %rsi - movq 0x38(%rdi), %rbp - /* %rsp is loaded by vmrun from VMCB */ - movq 0x48(%rdi), %r8 - movq 0x50(%rdi), %r9 - movq 0x58(%rdi), %r10 - movq 0x60(%rdi), %r11 - movq 0x68(%rdi), %r12 - movq 0x70(%rdi), %r13 - movq 0x78(%rdi), %r14 - movq 0x80(%rdi), %r15 - movq 0x28(%rdi), %rdi + load_vcpu_regs + /* %rax = vcpu->vmcb; */ + movq (%rsp), %rax + movq (%rax), %rax vmload vmrun vmsave - /* Save guest registers */ - movq %rdi, %rax + save_vcpu_regs popq %rdi - movq %rbx, 0x10(%rdi) - movq %rcx, 0x18(%rdi) - movq %rdx, 0x20(%rdi) - /* %rax contains guest %rdi */ - movq %rax, 0x28(%rdi) - movq %rsi, 0x30(%rdi) - movq %rbp, 0x38(%rdi) - movq %r8, 0x48(%rdi) - movq %r9, 0x50(%rdi) - movq %r10, 0x58(%rdi) - movq %r11, 0x60(%rdi) - movq %r12, 0x68(%rdi) - movq %r13, 0x70(%rdi) - movq %r14, 0x78(%rdi) - movq %r15, 0x80(%rdi) + /* copy guest %rax and %rsp from VMCB*/ movq (%rdi), %rsi movq 0x5f8(%rsi), %rax @@ -555,15 +587,60 @@ kvm_svm_vmrun: vmload stgi - - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx + pop_local retq +.global kvm_vmx_vmlaunch +kvm_vmx_vmlaunch: + push_local + pushq %rdi + + mov $VMX_VMCS_HOST_RSP, %rax + vmwrite %rsp, %rax + jna vmx_vmwrite_error + mov $VMX_VMCS_HOST_RIP, %rax + lea vmx_vm_exit, %rbx + vmwrite %rbx, %rax + jna vmx_vmwrite_error + + load_vcpu_regs + vmlaunch + jmp vmx_vm_exit + +.global kvm_vmx_vmresume +kvm_vmx_vmresume: + push_local + pushq %rdi + + movq $VMX_VMCS_HOST_RSP, %rax + vmwrite %rsp, %rax + jna vmx_vmwrite_error + movq $VMX_VMCS_HOST_RIP, %rax + lea vmx_vm_exit, %rbx + vmwrite %rbx, %rax + jna vmx_vmwrite_error + + load_vcpu_regs + vmresume + +vmx_vm_exit: + jna vmx_vmentry_error + save_vcpu_regs + xorq %rax, %rax + +vmx_vm_ret: + popq %rdi + pop_local + retq + +vmx_vmwrite_error: + movq $2, %rax + jmp vmx_vm_ret + +vmx_vmentry_error: + movq $1, %rax + jmp vmx_vm_ret + .section .bss.pgtables, "aw", @nobits .global kvm_pagetable kvm_pagetable: diff --git a/testcases/kernel/kvm/include/kvm_guest.h b/testcases/kernel/kvm/include/kvm_guest.h index 96f24615..3f3e2f16 100644 --- a/testcases/kernel/kvm/include/kvm_guest.h +++ b/testcases/kernel/kvm/include/kvm_guest.h @@ -8,6 +8,8 @@ #ifndef KVM_GUEST_H_ #define KVM_GUEST_H_ +#include + /* The main LTP include dir is intentionally excluded during payload build */ #include "../../../../include/tst_res_flags.h" #undef TERRNO @@ -46,9 +48,16 @@ void *memset(void *dest, int val, size_t size); void *memzero(void *dest, size_t size); void *memcpy(void *dest, const void *src, size_t size); +int memcmp(const void *a, const void *b, size_t length); + char *strcpy(char *dest, const char *src); char *strcat(char *dest, const char *src); size_t strlen(const char *str); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); + +int vsprintf(char *dest, const char *fmt, va_list ap); +int sprintf(char *dest, const char *fmt, ...); /* Exit the VM by looping on a HLT instruction forever */ void kvm_exit(void) __attribute__((noreturn)); @@ -57,12 +66,16 @@ void kvm_exit(void) __attribute__((noreturn)); void kvm_yield(void); void tst_res_(const char *file, const int lineno, int result, - const char *message); -#define tst_res(result, msg) tst_res_(__FILE__, __LINE__, (result), (msg)) + const char *fmt, ...) + __attribute__ ((format (printf, 4, 5))); +#define tst_res(result, fmt, ...) \ + tst_res_(__FILE__, __LINE__, (result), (fmt), ##__VA_ARGS__) void tst_brk_(const char *file, const int lineno, int result, - const char *message) __attribute__((noreturn)); -#define tst_brk(result, msg) tst_brk_(__FILE__, __LINE__, (result), (msg)) + const char *fmt, ...) __attribute__((noreturn)) + __attribute__ ((format (printf, 4, 5))); +#define tst_brk(result, fmt, ...) \ + tst_brk_(__FILE__, __LINE__, (result), (fmt), ##__VA_ARGS__) /* * Send asynchronous notification to host without stopping VM execution and diff --git a/testcases/kernel/kvm/include/kvm_x86.h b/testcases/kernel/kvm/include/kvm_x86.h index bc36c0e0..296dc385 100644 --- a/testcases/kernel/kvm/include/kvm_x86.h +++ b/testcases/kernel/kvm/include/kvm_x86.h @@ -62,13 +62,25 @@ /* CPUID constants */ +#define CPUID_GET_MODEL_INFO 0x1 #define CPUID_GET_INPUT_RANGE 0x80000000 #define CPUID_GET_EXT_FEATURES 0x80000001 #define CPUID_GET_SVM_FEATURES 0x8000000a /* Model-specific CPU register constants */ +#define MSR_IA32_FEATURE_CONTROL 0x3a +#define MSR_SYSENTER_CS 0x174 +#define MSR_SYSENTER_ESP 0x175 +#define MSR_SYSENTER_EIP 0x176 #define MSR_EFER 0xc0000080 +#define MSR_STAR 0xc0000081 +#define MSR_LSTAR 0xc0000082 +#define MSR_CSTAR 0xc0000083 +#define MSR_SFMASK 0xc0000084 +#define MSR_FS_BASE 0xc0000100 +#define MSR_GS_BASE 0xc0000101 +#define MSR_KERNEL_GS_BASE 0xc0000102 #define MSR_VM_CR 0xc0010114 #define MSR_VM_HSAVE_PA 0xc0010117 @@ -85,6 +97,18 @@ #define VM_CR_SVMDIS (1 << 4) /* Control register constants */ +#define CR0_PE (1 << 0) +#define CR0_MP (1 << 1) +#define CR0_EM (1 << 2) +#define CR0_TS (1 << 3) +#define CR0_ET (1 << 4) +#define CR0_NE (1 << 5) +#define CR0_WP (1 << 16) +#define CR0_AM (1 << 18) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) +#define CR0_PG (1 << 31) + #define CR4_VME (1 << 0) #define CR4_PVI (1 << 1) #define CR4_TSD (1 << 2) @@ -168,7 +192,7 @@ struct kvm_cregs { }; struct kvm_sregs { - uint16_t cs, ds, es, fs, gs, ss; + uint16_t cs, ds, es, fs, gs, ss, tr; }; struct kvm_regs64 { @@ -197,6 +221,9 @@ unsigned int kvm_create_stack_descriptor(struct segment_descriptor *table, void kvm_get_cpuid(unsigned int eax, unsigned int ecx, struct kvm_cpuid *buf); void kvm_read_cregs(struct kvm_cregs *buf); void kvm_read_sregs(struct kvm_sregs *buf); +void kvm_set_cr0(unsigned long val); +void kvm_set_cr3(unsigned long val); +void kvm_set_cr4(unsigned long val); uint64_t kvm_rdmsr(unsigned int msr); void kvm_wrmsr(unsigned int msr, uint64_t value); diff --git a/testcases/kernel/kvm/include/kvm_x86_svm.h b/testcases/kernel/kvm/include/kvm_x86_svm.h index b4b1b80e..73563ed2 100644 --- a/testcases/kernel/kvm/include/kvm_x86_svm.h +++ b/testcases/kernel/kvm/include/kvm_x86_svm.h @@ -163,4 +163,10 @@ struct kvm_svm_vcpu *kvm_create_svm_vcpu(int (*guest_main)(void), void kvm_svm_vmrun(struct kvm_svm_vcpu *cpu); +/* Load FS, GS, TR and LDTR state from vmsave_buf */ +void kvm_svm_vmload(struct kvm_vmcb *buf); + +/* Save current FS, GS, TR and LDTR state to vmsave_buf */ +void kvm_svm_vmsave(struct kvm_vmcb *buf); + #endif /* KVM_X86_SVM_H_ */ diff --git a/testcases/kernel/kvm/include/kvm_x86_vmx.h b/testcases/kernel/kvm/include/kvm_x86_vmx.h new file mode 100644 index 00000000..672c1b93 --- /dev/null +++ b/testcases/kernel/kvm/include/kvm_x86_vmx.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC + * + * x86-specific KVM helper functions and structures for Intel VMX + */ + +#ifndef KVM_X86_VMX_H_ +#define KVM_X86_VMX_H_ + +#include "kvm_x86.h" + +/* CPUID_GET_MODEL_INFO flags returned in ECX */ +#define CPUID_MODEL_VMX (1 << 5) +#define CPUID_MODEL_SMX (1 << 6) + +#define MSR_IA32_VMX_BASIC 0x480 +#define MSR_IA32_VMX_PINX_MASK 0x481 +#define MSR_IA32_VMX_EXECCTL_MASK 0x482 +#define MSR_IA32_VMX_EXITCTL_MASK 0x483 +#define MSR_IA32_VMX_ENTRYCTL_MASK 0x484 +#define MSR_IA32_VMX_CR0_FIXED0 0x486 +#define MSR_IA32_VMX_CR0_FIXED1 0x487 +#define MSR_IA32_VMX_CR4_FIXED0 0x488 +#define MSR_IA32_VMX_CR4_FIXED1 0x489 +#define MSR_IA32_VMX_EXECCTL2_MASK 0x48b +#define MSR_IA32_VMX_PINX_MASK2 0x48d +#define MSR_IA32_VMX_EXECCTL_MASK2 0x48e +#define MSR_IA32_VMX_EXITCTL_MASK2 0x48f +#define MSR_IA32_VMX_ENTRYCTL_MASK2 0x490 +#define MSR_IA32_VMX_EXECCTL3_MASK 0x492 + +#define VMX_CTLMASK_PINX 0 +#define VMX_CTLMASK_EXECCTL 1 +#define VMX_CTLMASK_EXECCTL2 2 +#define VMX_CTLMASK_EXECCTL3 3 +#define VMX_CTLMASK_EXITCTL 4 +#define VMX_CTLMASK_ENTRYCTL 5 +#define VMX_CTLMASK_MAX 6 + +#define IA32FC_LOCK (1 << 0) +#define IA32FC_VMXON_SMX (1 << 1) +#define IA32FC_VMXON_NORMAL (1 << 2) + +#define IA32_VMXBASIC_USELESS_CTL_MASKS (1ULL << 55) + +#define VMX_VMCS_GUEST_ES 0x800 +#define VMX_VMCS_GUEST_CS 0x802 +#define VMX_VMCS_GUEST_SS 0x804 +#define VMX_VMCS_GUEST_DS 0x806 +#define VMX_VMCS_GUEST_FS 0x808 +#define VMX_VMCS_GUEST_GS 0x80a +#define VMX_VMCS_GUEST_LDTR 0x80c +#define VMX_VMCS_GUEST_TR 0x80e +#define VMX_VMCS_GUEST_INTR 0x810 +#define VMX_VMCS_HOST_ES 0xc00 +#define VMX_VMCS_HOST_CS 0xc02 +#define VMX_VMCS_HOST_SS 0xc04 +#define VMX_VMCS_HOST_DS 0xc06 +#define VMX_VMCS_HOST_FS 0xc08 +#define VMX_VMCS_HOST_GS 0xc0a +#define VMX_VMCS_HOST_TR 0xc0c + +#define VMX_VMCS_MSR_BITMAP_POINTER 0x2004 +#define VMX_VMCS_VIRT_APIC_POINTER 0x2012 +#define VMX_VMCS_VIRT_APIC_BASE 0x2014 +#define VMX_VMCS_LINK_POINTER 0x2800 + +#define VMX_VMCS_GUEST_ES_LIMIT 0x4800 +#define VMX_VMCS_GUEST_CS_LIMIT 0x4802 +#define VMX_VMCS_GUEST_SS_LIMIT 0x4804 +#define VMX_VMCS_GUEST_DS_LIMIT 0x4806 +#define VMX_VMCS_GUEST_FS_LIMIT 0x4808 +#define VMX_VMCS_GUEST_GS_LIMIT 0x480a +#define VMX_VMCS_GUEST_LDTR_LIMIT 0x480c +#define VMX_VMCS_GUEST_TR_LIMIT 0x480e +#define VMX_VMCS_GUEST_GDTR_LIMIT 0x4810 +#define VMX_VMCS_GUEST_IDTR_LIMIT 0x4812 +#define VMX_VMCS_GUEST_ES_ACCESS 0x4814 +#define VMX_VMCS_GUEST_CS_ACCESS 0x4816 +#define VMX_VMCS_GUEST_SS_ACCESS 0x4818 +#define VMX_VMCS_GUEST_DS_ACCESS 0x481a +#define VMX_VMCS_GUEST_FS_ACCESS 0x481c +#define VMX_VMCS_GUEST_GS_ACCESS 0x481e +#define VMX_VMCS_GUEST_LDTR_ACCESS 0x4820 +#define VMX_VMCS_GUEST_TR_ACCESS 0x4822 +#define VMX_VMCS_GUEST_INTR_STATE 0x4824 +#define VMX_VMCS_GUEST_ACT_STATE 0x4826 +#define VMX_VMCS_GUEST_SMBASE 0x4828 +#define VMX_VMCS_GUEST_SYSENTER_CS 0x482a +#define VMX_VMCS_HOST_SYSENTER_CS 0x4c00 + +#define VMX_VMCS_GUEST_CR0 0x6800 +#define VMX_VMCS_GUEST_CR3 0x6802 +#define VMX_VMCS_GUEST_CR4 0x6804 +#define VMX_VMCS_GUEST_ES_BASE 0x6806 +#define VMX_VMCS_GUEST_CS_BASE 0x6808 +#define VMX_VMCS_GUEST_SS_BASE 0x680a +#define VMX_VMCS_GUEST_DS_BASE 0x680c +#define VMX_VMCS_GUEST_FS_BASE 0x680e +#define VMX_VMCS_GUEST_GS_BASE 0x6810 +#define VMX_VMCS_GUEST_LDTR_BASE 0x6812 +#define VMX_VMCS_GUEST_TR_BASE 0x6814 +#define VMX_VMCS_GUEST_GDTR_BASE 0x6816 +#define VMX_VMCS_GUEST_IDTR_BASE 0x6818 +#define VMX_VMCS_GUEST_DR7 0x681a +#define VMX_VMCS_GUEST_RSP 0x681c +#define VMX_VMCS_GUEST_RIP 0x681e +#define VMX_VMCS_GUEST_RFLAGS 0x6820 +#define VMX_VMCS_GUEST_DEBUG_EXC 0x6822 +#define VMX_VMCS_GUEST_SYSENTER_ESP 0x6824 +#define VMX_VMCS_GUEST_SYSENTER_EIP 0x6826 +#define VMX_VMCS_HOST_CR0 0x6c00 +#define VMX_VMCS_HOST_CR3 0x6c02 +#define VMX_VMCS_HOST_CR4 0x6c04 +#define VMX_VMCS_HOST_FS_BASE 0x6c06 +#define VMX_VMCS_HOST_GS_BASE 0x6c08 +#define VMX_VMCS_HOST_TR_BASE 0x6c0a +#define VMX_VMCS_HOST_GDTR_BASE 0x6c0c +#define VMX_VMCS_HOST_IDTR_BASE 0x6c0e +#define VMX_VMCS_HOST_SYSENTER_ESP 0x6c10 +#define VMX_VMCS_HOST_SYSENTER_EIP 0x6c12 +#define VMX_VMCS_HOST_RSP 0x6c14 +#define VMX_VMCS_HOST_RIP 0x6c16 + +#define VMX_VMCS_VMPINX_CTL 0x4000 +#define VMX_VMCS_VMEXEC_CTL 0x4002 +#define VMX_VMCS_VMEXIT_CTL 0x400c +#define VMX_VMCS_VMEXIT_MSR_STORE 0x400e +#define VMX_VMCS_VMEXIT_MSR_LOAD 0x4010 +#define VMX_VMCS_VMENTRY_CTL 0x4012 +#define VMX_VMCS_VMENTRY_MSR_LOAD 0x4014 +#define VMX_VMCS_VMENTRY_INTR 0x4016 +#define VMX_VMCS_VMENTRY_EXC 0x4018 +#define VMX_VMCS_VMENTRY_INST_LEN 0x401a +#define VMX_VMCS_VMEXEC_CTL2 0x401e + +#define VMX_VMCS_VMINST_ERROR 0x4400 +#define VMX_VMCS_EXIT_REASON 0x4402 +#define VMX_VMCS_VMEXIT_INTR_INFO 0x4404 +#define VMX_VMCS_VMEXIT_INTR_ERRNO 0x4406 +#define VMX_VMCS_IDTVEC_INFO 0x4408 +#define VMX_VMCS_IDTVEC_ERRNO 0x440a +#define VMX_VMCS_VMEXIT_INST_LEN 0x440c +#define VMX_VMCS_VMEXIT_INST_INFO 0x440e +#define VMX_VMCS_EXIT_QUALIFICATION 0x6400 + +#define VMX_INTERCEPT_HLT (1 << 7) +#define VMX_EXECCTL_TPR_SHADOW (1 << 21) +#define VMX_EXECCTL_MSR_BITMAP (1 << 28) +#define VMX_EXECCTL_ENABLE_CTL2 (1 << 31) + +#define VMX_EXECCTL2_VIRT_APIC (1 << 0) +#define VMX_EXECCTL2_VIRT_X2APIC (1 << 4) +#define VMX_EXECCTL2_VIRT_APIC_REG (1 << 8) +#define VMX_EXECCTL2_VIRT_INTR (1 << 9) +#define VMX_EXECCTL2_SHADOW_VMCS (1 << 14) + +#define VMX_EXITCTL_SAVE_DR (1 << 2) +#define VMX_EXITCTL_X64 (1 << 9) + +#define VMX_ENTRYCTL_LOAD_DR (1 << 2) +#define VMX_ENTRYCTL_X64 (1 << 9) + +#define VMX_SHADOW_VMCS 0x80000000 +#define VMX_VMCSFIELD_64BIT 0x2000 +#define VMX_VMCSFIELD_SIZE_MASK 0x6000 + +#define VMX_INVALID_VMCS 0xffffffffffffffffULL + +#define VMX_EXIT_HLT 12 +#define VMX_EXIT_RDMSR 31 +#define VMX_EXIT_FAILED_ENTRY 0x80000000 + +struct kvm_vmcs { + uint32_t version; + uint32_t abort; + uint8_t data[4088]; +}; + +struct kvm_vmx_vcpu { + struct kvm_vmcs *vmcs; + struct kvm_regs64 regs; + int launched; +}; + +/* Intel VMX virtualization helper functions */ +int kvm_is_vmx_supported(void); +void kvm_set_vmx_state(int enabled); +struct kvm_vmcs *kvm_alloc_vmcs(void); + +/* Copy GDT entry to given fields of the current VMCS */ +void kvm_vmcs_copy_gdt_descriptor(unsigned int gdt_id, + unsigned long vmcs_selector, unsigned long vmcs_flags, + unsigned long vmcs_limit, unsigned long vmcs_baseaddr); +uint64_t kvm_vmx_read_vmctl_mask(unsigned int ctl_id); +void kvm_init_vmx_vcpu(struct kvm_vmx_vcpu *cpu, uint16_t ss, void *rsp, + int (*guest_main)(void)); +struct kvm_vmx_vcpu *kvm_create_vmx_vcpu(int (*guest_main)(void), + int alloc_stack); + +/* Set the VMCS as current and update the host state fields */ +void kvm_vmx_activate_vcpu(struct kvm_vmx_vcpu *cpu); +void kvm_vmx_vmrun(struct kvm_vmx_vcpu *cpu); + +void kvm_vmx_vmclear(struct kvm_vmcs *buf); +void kvm_vmx_vmptrld(struct kvm_vmcs *buf); +uint64_t kvm_vmx_vmptrst(void); +uint64_t kvm_vmx_vmread(unsigned long var_id); +void kvm_vmx_vmwrite(unsigned long var_id, uint64_t value); +int kvm_vmx_vmlaunch(struct kvm_vmx_vcpu *buf); +int kvm_vmx_vmresume(struct kvm_vmx_vcpu *buf); + +/* Read last VMX instruction error from current VMCS */ +int kvm_vmx_inst_errno(void); +/* Get VMX instruction error description */ +const char *kvm_vmx_inst_strerr(int vmx_errno); +/* Get description of last VMX instruction error in current VMCS */ +const char *kvm_vmx_inst_err(void); + +#endif /* KVM_X86_VMX_H_ */ diff --git a/testcases/kernel/kvm/kvm_pagefault01.c b/testcases/kernel/kvm/kvm_pagefault01.c index 91891848..db526cb7 100644 --- a/testcases/kernel/kvm/kvm_pagefault01.c +++ b/testcases/kernel/kvm/kvm_pagefault01.c @@ -136,73 +136,22 @@ TST_TEST_TCONF("Test supported only on x86_64"); #else /* COMPILE_PAYLOAD */ -#include -#include -#include #include "tst_module.h" #define TDP_MMU_SYSFILE "/sys/module/kvm/parameters/tdp_mmu" #define TDP_AMD_SYSFILE "/sys/module/kvm_amd/parameters/npt" #define TDP_INTEL_SYSFILE "/sys/module/kvm_intel/parameters/ept" -#define BUF_SIZE 64 - -static int read_bool_sys_param(const char *filename) -{ - char buf[BUF_SIZE]; - int i, fd, ret; - - fd = open(filename, O_RDONLY); - - if (fd < 0) - return -1; - - ret = read(fd, buf, BUF_SIZE - 1); - SAFE_CLOSE(fd); - - if (ret < 1) - return -1; - - buf[ret] = '\0'; - - for (i = 0; buf[i] && !isspace(buf[i]); i++) - ; - - buf[i] = '\0'; - - if (isdigit(buf[0])) { - tst_parse_int(buf, &ret, INT_MIN, INT_MAX); - return ret; - } - - if (!strcasecmp(buf, "N")) - return 0; - - /* Assume that any other value than 0 or N means the param is enabled */ - return 1; -} - -static void reload_module(const char *module, char *arg) -{ - const char *const argv[] = {"modprobe", module, arg, NULL}; - - tst_res(TINFO, "Reloading module %s with parameter %s", module, arg); - tst_module_unload(module); - tst_cmd(argv, NULL, NULL, 0); -} - static void disable_tdp(void) { - if (!access(TDP_MMU_SYSFILE, F_OK)) { - /* FIXME: Is setting tdp_mmu=0 sufficient to disable TDP? */ - return; - } + if (tst_read_bool_sys_param(TDP_AMD_SYSFILE) > 0) + tst_module_reload("kvm_amd", (char *const[]){"npt=0", NULL}); - if (read_bool_sys_param(TDP_AMD_SYSFILE) > 0) - reload_module("kvm_amd", "npt=0"); + if (tst_read_bool_sys_param(TDP_INTEL_SYSFILE) > 0) + tst_module_reload("kvm_intel", (char *const[]){"ept=0", NULL}); - if (read_bool_sys_param(TDP_INTEL_SYSFILE) > 0) - reload_module("kvm_intel", "ept=0"); + if (tst_read_bool_sys_param(TDP_MMU_SYSFILE) > 0) + tst_res(TINFO, "WARNING: tdp_mmu is enabled, beware of false negatives"); } static void setup(void) @@ -216,11 +165,6 @@ static struct tst_test test = { .setup = setup, .cleanup = tst_kvm_cleanup, .needs_root = 1, - .save_restore = (const struct tst_path_val[]) { - {"/sys/module/kvm/parameters/tdp_mmu", "0", - TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, - {} - }, .supported_archs = (const char *const []) { "x86_64", NULL diff --git a/testcases/kernel/kvm/kvm_svm02.c b/testcases/kernel/kvm/kvm_svm02.c index 5d2e2ce3..6914fdcb 100644 --- a/testcases/kernel/kvm/kvm_svm02.c +++ b/testcases/kernel/kvm/kvm_svm02.c @@ -33,22 +33,14 @@ static void *vmsave_buf; /* Load FS, GS, TR and LDTR state from vmsave_buf */ static int guest_vmload(void) { - asm ( - "vmload %0\n" - : - : "a" (vmsave_buf) - ); + kvm_svm_vmload(vmsave_buf); return 0; } /* Save current FS, GS, TR and LDTR state to vmsave_buf */ static int guest_vmsave(void) { - asm ( - "vmsave %0\n" - : - : "a" (vmsave_buf) - ); + kvm_svm_vmsave(vmsave_buf); return 0; } @@ -96,7 +88,7 @@ void main(void) vmsave_buf = kvm_alloc_vmcb(); /* Save allocated stack for later VM reinit */ - ss = vcpu->vmcb->ss.selector; + ss = vcpu->vmcb->ss.selector >> 3; rsp = vcpu->vmcb->rsp; /* Load partial state from vmsave_buf and save it to vcpu->vmcb */ diff --git a/testcases/kernel/kvm/kvm_svm04.c b/testcases/kernel/kvm/kvm_svm04.c new file mode 100644 index 00000000..75fcbfdc --- /dev/null +++ b/testcases/kernel/kvm/kvm_svm04.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 SUSE LLC + */ + +/*\ + * Functional test for VMSAVE/VMLOAD instructions in KVM environment. Verify + * that both instructions save/load the CPU state according to CPU + * documentation. + */ + +#include "kvm_test.h" + +#ifdef COMPILE_PAYLOAD +#if defined(__i386__) || defined(__x86_64__) + +#include "kvm_x86_svm.h" + +static struct kvm_vmcb *src_vmcb, *dest_vmcb, *msr_vmcb; +static struct kvm_sregs sregs_buf; + +static int check_descriptor(const char *name, + const struct kvm_vmcb_descriptor *data, + const struct kvm_vmcb_descriptor *exp) +{ + int ret = 0; + + if (data->selector != exp->selector) { + tst_res(TFAIL, "%s.selector = %hx (expected %hx)", + name, data->selector, exp->selector); + ret = 1; + } + + if (data->attrib != exp->attrib) { + tst_res(TFAIL, "%s.attrib = 0x%hx (expected 0x%hx)", + name, data->attrib, exp->attrib); + ret = 1; + } + + if (data->limit != exp->limit) { + tst_res(TFAIL, "%s.limit = 0x%x (expected 0x%x)", + name, data->limit, exp->limit); + ret = 1; + } + + if (data->base != exp->base) { + tst_res(TFAIL, "%s.base = 0x%llx (expected 0x%llx)", + name, data->base, exp->base); + ret = 1; + } + + return ret; +} + +static int check_value(const char *name, uint64_t val, uint64_t exp, + uint64_t backup, uint64_t reg, uint64_t nested_val) +{ + int ret = 0; + + if (exp != backup) { + tst_res(TFAIL, "%s source was modified (0x%llx != 0x%llx)", + name, exp, backup); + ret = 1; + } + + if (reg != exp) { + tst_res(TFAIL, "%s was not loaded (0x%llx != 0x%llx)", + name, reg, exp); + ret = 1; + } + + if (val != exp) { + tst_res(TFAIL, "%s was not saved (0x%llx != 0x%llx)", + name, val, exp); + ret = 1; + } + + if (val != nested_val) { + tst_res(TFAIL, "Inconsistent %s on VM exit (0x%llx != 0x%llx)", + name, val, nested_val); + ret = 1; + } + + if (!ret) + tst_res(TPASS, "%s has correct value 0x%llx", name, val); + + return ret; +} + +static int vmsave_copy(void) +{ + kvm_svm_vmload(src_vmcb); + kvm_read_sregs(&sregs_buf); + msr_vmcb->star = kvm_rdmsr(MSR_STAR); + msr_vmcb->lstar = kvm_rdmsr(MSR_LSTAR); + msr_vmcb->cstar = kvm_rdmsr(MSR_CSTAR); + msr_vmcb->sfmask = kvm_rdmsr(MSR_SFMASK); + msr_vmcb->fs.base = kvm_rdmsr(MSR_FS_BASE); + msr_vmcb->gs.base = kvm_rdmsr(MSR_GS_BASE); + msr_vmcb->kernel_gs_base = kvm_rdmsr(MSR_KERNEL_GS_BASE); + msr_vmcb->sysenter_cs = kvm_rdmsr(MSR_SYSENTER_CS); + msr_vmcb->sysenter_esp = kvm_rdmsr(MSR_SYSENTER_ESP); + msr_vmcb->sysenter_eip = kvm_rdmsr(MSR_SYSENTER_EIP); + kvm_svm_vmsave(dest_vmcb); + return 0; +} + +static int check_vmsave_result(struct kvm_vmcb *copy_vmcb, + struct kvm_vmcb *nested_vmcb) +{ + int ret = 0; + + /* Nested VMCB is only compared to dest VMCB, bypass the check */ + if (!nested_vmcb) + nested_vmcb = dest_vmcb; + + ret = check_descriptor("FS", &dest_vmcb->fs, &src_vmcb->fs); + ret = check_value("FS.selector", dest_vmcb->fs.selector, + src_vmcb->fs.selector, copy_vmcb->fs.selector, + sregs_buf.fs, nested_vmcb->fs.selector) || ret; + ret = check_descriptor("GS", &dest_vmcb->gs, &src_vmcb->gs) || ret; + ret = check_value("GS.selector", dest_vmcb->gs.selector, + src_vmcb->gs.selector, copy_vmcb->gs.selector, + sregs_buf.gs, nested_vmcb->gs.selector) || ret; + ret = check_descriptor("LDTR", &dest_vmcb->ldtr, &src_vmcb->ldtr) || + ret; + ret = check_descriptor("TR", &dest_vmcb->tr, &src_vmcb->tr) || ret; + ret = check_value("STAR", dest_vmcb->star, src_vmcb->star, + copy_vmcb->star, msr_vmcb->star, nested_vmcb->star) || ret; + ret = check_value("LSTAR", dest_vmcb->lstar, src_vmcb->lstar, + copy_vmcb->lstar, msr_vmcb->lstar, nested_vmcb->lstar) || ret; + ret = check_value("CSTAR", dest_vmcb->cstar, src_vmcb->cstar, + copy_vmcb->cstar, msr_vmcb->cstar, nested_vmcb->cstar) || ret; + ret = check_value("SFMASK", dest_vmcb->sfmask, src_vmcb->sfmask, + copy_vmcb->sfmask, msr_vmcb->sfmask, nested_vmcb->sfmask) || + ret; + ret = check_value("FS.base", dest_vmcb->fs.base, src_vmcb->fs.base, + copy_vmcb->fs.base, msr_vmcb->fs.base, nested_vmcb->fs.base) || + ret; + ret = check_value("GS.base", dest_vmcb->gs.base, src_vmcb->gs.base, + copy_vmcb->gs.base, msr_vmcb->gs.base, nested_vmcb->gs.base) || + ret; + ret = check_value("KernelGSBase", dest_vmcb->kernel_gs_base, + src_vmcb->kernel_gs_base, copy_vmcb->kernel_gs_base, + msr_vmcb->kernel_gs_base, nested_vmcb->kernel_gs_base) || ret; + ret = check_value("Sysenter_CS", dest_vmcb->sysenter_cs, + src_vmcb->sysenter_cs, copy_vmcb->sysenter_cs, + msr_vmcb->sysenter_cs, nested_vmcb->sysenter_cs) || ret; + ret = check_value("Sysenter_ESP", dest_vmcb->sysenter_esp, + src_vmcb->sysenter_esp, copy_vmcb->sysenter_esp, + msr_vmcb->sysenter_esp, nested_vmcb->sysenter_esp) || ret; + ret = check_value("Sysenter_EIP", dest_vmcb->sysenter_eip, + src_vmcb->sysenter_eip, copy_vmcb->sysenter_eip, + msr_vmcb->sysenter_eip, nested_vmcb->sysenter_eip) || ret; + + return ret; +} + +static int create_segment_descriptor(uint64_t baseaddr, uint32_t limit, + unsigned int flags) +{ + int ret = kvm_find_free_descriptor(kvm_gdt, KVM_GDT_SIZE); + + if (ret < 0) + tst_brk(TBROK, "Descriptor table is full"); + + kvm_set_segment_descriptor(kvm_gdt + ret, baseaddr, limit, flags); + return ret; +} + +static void dirty_vmcb(struct kvm_vmcb *buf) +{ + buf->fs.selector = 0x60; + buf->fs.attrib = SEGTYPE_RWDATA | SEGFLAG_PRESENT; + buf->fs.limit = 0xffff; + buf->fs.base = 0xfff000; + buf->gs.selector = 0x68; + buf->gs.attrib = SEGTYPE_RWDATA | SEGFLAG_PRESENT; + buf->gs.limit = 0xffff; + buf->gs.base = 0xfff000; + buf->ldtr.selector = 0x70; + buf->ldtr.attrib = SEGTYPE_LDT | SEGFLAG_PRESENT; + buf->ldtr.limit = 0xffff; + buf->ldtr.base = 0xfff000; + buf->tr.selector = 0x78; + buf->tr.attrib = SEGTYPE_TSS | SEGFLAG_PRESENT; + buf->tr.limit = 0xffff; + buf->tr.base = 0xfff000; + buf->star = 0xffff; + buf->lstar = 0xffff; + buf->cstar = 0xffff; + buf->sfmask = 0xffff; + buf->fs.base = 0xffff; + buf->gs.base = 0xffff; + buf->kernel_gs_base = 0xffff; + buf->sysenter_cs = 0xffff; + buf->sysenter_esp = 0xffff; + buf->sysenter_eip = 0xffff; +} + +void main(void) +{ + uint16_t ss; + uint64_t rsp; + struct kvm_svm_vcpu *vcpu; + int data_seg1, data_seg2, ldt_seg, task_seg; + struct segment_descriptor *ldt; + struct kvm_vmcb *backup_vmcb, *zero_vmcb; + unsigned int ldt_size = KVM_GDT_SIZE*sizeof(struct segment_descriptor); + + kvm_init_svm(); + + src_vmcb = kvm_alloc_vmcb(); + dest_vmcb = kvm_alloc_vmcb(); + msr_vmcb = kvm_alloc_vmcb(); + backup_vmcb = kvm_alloc_vmcb(); + zero_vmcb = kvm_alloc_vmcb(); + + vcpu = kvm_create_svm_vcpu(vmsave_copy, 1); + kvm_vmcb_set_intercept(vcpu->vmcb, SVM_INTERCEPT_VMLOAD, 0); + kvm_vmcb_set_intercept(vcpu->vmcb, SVM_INTERCEPT_VMSAVE, 0); + /* Save allocated stack for later VM reinit */ + ss = vcpu->vmcb->ss.selector >> 3; + rsp = vcpu->vmcb->rsp; + + ldt = tst_heap_alloc_aligned(ldt_size, 8); + memset(ldt, 0, ldt_size); + data_seg1 = create_segment_descriptor(0xda7a1000, 0x1000, + SEGTYPE_RODATA | SEGFLAG_PRESENT); + data_seg2 = create_segment_descriptor(0xda7a2000, 2, + SEGTYPE_RWDATA | SEGFLAG_PRESENT | SEGFLAG_PAGE_LIMIT); + ldt_seg = create_segment_descriptor((uintptr_t)ldt, ldt_size, + SEGTYPE_LDT | SEGFLAG_PRESENT); + task_seg = create_segment_descriptor(0x7a53000, 0x1000, + SEGTYPE_TSS | SEGFLAG_PRESENT); + kvm_vmcb_copy_gdt_descriptor(&src_vmcb->fs, data_seg1); + kvm_vmcb_copy_gdt_descriptor(&src_vmcb->gs, data_seg2); + kvm_vmcb_copy_gdt_descriptor(&src_vmcb->ldtr, ldt_seg); + kvm_vmcb_copy_gdt_descriptor(&src_vmcb->tr, task_seg); + + src_vmcb->star = 0x5742; + src_vmcb->lstar = 0x15742; + src_vmcb->cstar = 0xc5742; + src_vmcb->sfmask = 0xf731; + src_vmcb->fs.base = 0xf000; + src_vmcb->gs.base = 0x10000; + src_vmcb->kernel_gs_base = 0x20000; + src_vmcb->sysenter_cs = 0x595c5; + src_vmcb->sysenter_esp = 0x595e50; + src_vmcb->sysenter_eip = 0x595e10; + + memcpy(backup_vmcb, src_vmcb, sizeof(struct kvm_vmcb)); + tst_res(TINFO, "VMLOAD/VMSAVE non-zero values"); + vmsave_copy(); + check_vmsave_result(backup_vmcb, NULL); + + memset(src_vmcb, 0, sizeof(struct kvm_vmcb)); + tst_res(TINFO, "VMLOAD/VMSAVE zero values"); + dirty_vmcb(dest_vmcb); + vmsave_copy(); + check_vmsave_result(zero_vmcb, NULL); + + memcpy(src_vmcb, backup_vmcb, sizeof(struct kvm_vmcb)); + tst_res(TINFO, "Nested VMLOAD/VMSAVE non-zero values"); + dirty_vmcb(vcpu->vmcb); + memset(dest_vmcb, 0, sizeof(struct kvm_vmcb)); + kvm_svm_vmrun(vcpu); + + if (vcpu->vmcb->exitcode != SVM_EXIT_HLT) + tst_brk(TBROK, "Nested VM exited unexpectedly"); + + check_vmsave_result(backup_vmcb, vcpu->vmcb); + + memset(src_vmcb, 0, sizeof(struct kvm_vmcb)); + tst_res(TINFO, "Nested VMLOAD/VMSAVE zero values"); + kvm_init_guest_vmcb(vcpu->vmcb, 1, ss, (void *)rsp, vmsave_copy); + kvm_vmcb_set_intercept(vcpu->vmcb, SVM_INTERCEPT_VMLOAD, 0); + kvm_vmcb_set_intercept(vcpu->vmcb, SVM_INTERCEPT_VMSAVE, 0); + dirty_vmcb(vcpu->vmcb); + kvm_svm_vmrun(vcpu); + + if (vcpu->vmcb->exitcode != SVM_EXIT_HLT) + tst_brk(TBROK, "Nested VM exited unexpectedly"); + + check_vmsave_result(zero_vmcb, vcpu->vmcb); +} + +#else /* defined(__i386__) || defined(__x86_64__) */ +TST_TEST_TCONF("Test supported only on x86"); +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#else /* COMPILE_PAYLOAD */ + +static struct tst_test test = { + .test_all = tst_kvm_run, + .setup = tst_kvm_setup, + .cleanup = tst_kvm_cleanup, + .supported_archs = (const char *const []) { + "x86_64", + "x86", + NULL + }, +}; + +#endif /* COMPILE_PAYLOAD */ diff --git a/testcases/kernel/kvm/kvm_vmx01.c b/testcases/kernel/kvm/kvm_vmx01.c new file mode 100644 index 00000000..5bffbe94 --- /dev/null +++ b/testcases/kernel/kvm/kvm_vmx01.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC + */ + +/*\ + * Basic functional test for VMREAD/VMWRITE instructions in KVM environment. + * Verify that VMWRITE instruction changes the contents of current VMCS and + * the values written into shadow VMCS can be read in both parent and nested + * VM. + */ + +#include "kvm_test.h" + +#ifdef COMPILE_PAYLOAD +#if defined(__i386__) || defined(__x86_64__) + +#include "kvm_x86_vmx.h" + +#define GUEST_READ_ERROR 1 +#define GUEST_WRITE_ERROR 2 +#define SHADOW_DATA_LENGTH 37 +#define VMCS_FIELD(x) x, #x + +struct vmcs_field_table { + unsigned long field_id; + const char *name; + unsigned long value; +}; + +/* Data written into shadow VMCS by the parent VM and read by the nested VM */ +static struct vmcs_field_table host_data[SHADOW_DATA_LENGTH] = { + {VMCS_FIELD(VMX_VMCS_GUEST_ES), 0xe5}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS), 0xc5}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS), 0x55}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS), 0xd5}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS), 0xf5}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS), 0x65}, + {VMCS_FIELD(VMX_VMCS_GUEST_LDTR), 0x1d72}, + {VMCS_FIELD(VMX_VMCS_GUEST_TR), 0x72}, + {VMCS_FIELD(VMX_VMCS_HOST_ES), 0x5e}, + {VMCS_FIELD(VMX_VMCS_HOST_CS), 0x5c}, + {VMCS_FIELD(VMX_VMCS_HOST_SS), 0x55}, + {VMCS_FIELD(VMX_VMCS_HOST_DS), 0x5d}, + {VMCS_FIELD(VMX_VMCS_HOST_FS), 0x5f}, + {VMCS_FIELD(VMX_VMCS_HOST_GS), 0x56}, + {VMCS_FIELD(VMX_VMCS_HOST_TR), 0x27}, + {VMCS_FIELD(VMX_VMCS_GUEST_ES_LIMIT), 0xe51}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS_LIMIT), 0xc51}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS_LIMIT), 0x551}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS_LIMIT), 0xd51}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS_LIMIT), 0xf51}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS_LIMIT), 0x651}, + {VMCS_FIELD(VMX_VMCS_GUEST_LDTR_LIMIT), 0x1d721}, + {VMCS_FIELD(VMX_VMCS_GUEST_ES_ACCESS), 0xa0e5}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS_ACCESS), 0xa0c5}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS_ACCESS), 0xa055}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS_ACCESS), 0xa0d5}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS_ACCESS), 0xa0f5}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS_ACCESS), 0xa065}, + {VMCS_FIELD(VMX_VMCS_GUEST_SYSENTER_CS), 0x65c}, + {VMCS_FIELD(VMX_VMCS_HOST_SYSENTER_CS), 0x45c}, + {VMCS_FIELD(VMX_VMCS_GUEST_ES_BASE), 0xe5b}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS_BASE), 0xc5b}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS_BASE), 0x55b}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS_BASE), 0xd5b}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS_BASE), 0xf5b}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS_BASE), 0x65b}, + {VMCS_FIELD(VMX_VMCS_GUEST_LDTR_BASE), 0x1d72b} +}; + +/* Data written into shadow VMCS by the nested VM and read by the parent VM */ +static struct vmcs_field_table guest_data[SHADOW_DATA_LENGTH] = { + {VMCS_FIELD(VMX_VMCS_GUEST_ES), 0x5e}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS), 0x5c}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS), 0x55}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS), 0x5d}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS), 0x5f}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS), 0x56}, + {VMCS_FIELD(VMX_VMCS_GUEST_LDTR), 0x721d}, + {VMCS_FIELD(VMX_VMCS_GUEST_TR), 0x27}, + {VMCS_FIELD(VMX_VMCS_HOST_ES), 0xe5}, + {VMCS_FIELD(VMX_VMCS_HOST_CS), 0xc5}, + {VMCS_FIELD(VMX_VMCS_HOST_SS), 0x55}, + {VMCS_FIELD(VMX_VMCS_HOST_DS), 0xd5}, + {VMCS_FIELD(VMX_VMCS_HOST_FS), 0xf5}, + {VMCS_FIELD(VMX_VMCS_HOST_GS), 0x65}, + {VMCS_FIELD(VMX_VMCS_HOST_TR), 0x72}, + {VMCS_FIELD(VMX_VMCS_GUEST_ES_LIMIT), 0x1e5}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS_LIMIT), 0x1c5}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS_LIMIT), 0x155}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS_LIMIT), 0x1d5}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS_LIMIT), 0x1f5}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS_LIMIT), 0x165}, + {VMCS_FIELD(VMX_VMCS_GUEST_LDTR_LIMIT), 0x11d72}, + {VMCS_FIELD(VMX_VMCS_GUEST_ES_ACCESS), 0xa05e}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS_ACCESS), 0xa05c}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS_ACCESS), 0xa055}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS_ACCESS), 0xa05d}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS_ACCESS), 0xa05f}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS_ACCESS), 0xa056}, + {VMCS_FIELD(VMX_VMCS_GUEST_SYSENTER_CS), 0x5c6}, + {VMCS_FIELD(VMX_VMCS_HOST_SYSENTER_CS), 0x5c4}, + {VMCS_FIELD(VMX_VMCS_GUEST_ES_BASE), 0xbe5}, + {VMCS_FIELD(VMX_VMCS_GUEST_CS_BASE), 0xbc5}, + {VMCS_FIELD(VMX_VMCS_GUEST_SS_BASE), 0xb55}, + {VMCS_FIELD(VMX_VMCS_GUEST_DS_BASE), 0xbd5}, + {VMCS_FIELD(VMX_VMCS_GUEST_FS_BASE), 0xbf5}, + {VMCS_FIELD(VMX_VMCS_GUEST_GS_BASE), 0xb65}, + {VMCS_FIELD(VMX_VMCS_GUEST_LDTR_BASE), 0xb1d72} +}; + +static unsigned long vmread_buffer[SHADOW_DATA_LENGTH]; + +int guest_main(void) +{ + int i; + + /* kvm_vmx_vmread() calls tst_brk(), don't use it in nested VM */ + for (i = 0; i < SHADOW_DATA_LENGTH; i++) { + asm goto( + "vmread %1, (%0)\n" + "jna %l[read_error]\n" + "vmwrite %2, %3\n" + "jna %l[write_error]\n" + : + : "r" (&vmread_buffer[i]), "r" (host_data[i].field_id), + "r" (guest_data[i].value), + "r" (guest_data[i].field_id) + : "cc", "memory" + : read_error, write_error + ); + } + + return 0; + +read_error: + return GUEST_READ_ERROR; + +write_error: + return GUEST_WRITE_ERROR; +} + +void main(void) +{ + struct kvm_vmx_vcpu *vcpu; + struct kvm_vmcs *shadow_vmcs; + char *vmcs_backup; + int i, errors; + uint64_t val; + + kvm_set_vmx_state(1); + + /* Check secondary VMCS execctl support */ + val = kvm_vmx_read_vmctl_mask(VMX_CTLMASK_EXECCTL); + + if (!((val >> 32) & VMX_EXECCTL_ENABLE_CTL2)) + tst_brk(TCONF, "CPU does not support shadow VMCS"); + + /* Create and configure guest VMCS */ + shadow_vmcs = kvm_alloc_vmcs(); + kvm_vmx_vmclear(shadow_vmcs); + shadow_vmcs->version |= VMX_SHADOW_VMCS; + vcpu = kvm_create_vmx_vcpu(guest_main, 1); + kvm_vmx_vmptrld(vcpu->vmcs); + val = kvm_vmx_vmread(VMX_VMCS_VMEXEC_CTL); + val |= VMX_EXECCTL_ENABLE_CTL2; + kvm_vmx_vmwrite(VMX_VMCS_VMEXEC_CTL, val); + val = kvm_vmx_read_vmctl_mask(VMX_CTLMASK_EXECCTL2); + + if (!((val >> 32) & VMX_EXECCTL2_SHADOW_VMCS)) + tst_brk(TCONF, "CPU does not support shadow VMCS"); + + val = VMX_EXECCTL2_SHADOW_VMCS | (uint32_t)val; + kvm_vmx_vmwrite(VMX_VMCS_VMEXEC_CTL2, val); + kvm_vmx_vmwrite(VMX_VMCS_LINK_POINTER, (uintptr_t)shadow_vmcs); + + /* Configure shadow VMCS */ + vmcs_backup = tst_heap_alloc(sizeof(struct kvm_vmcs)); + memcpy(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs)); + kvm_vmx_vmptrld(shadow_vmcs); + + for (i = 0; i < SHADOW_DATA_LENGTH; i++) + kvm_vmx_vmwrite(host_data[i].field_id, host_data[i].value); + + /* Flush shadow VMCS just in case */ + kvm_vmx_vmptrld(vcpu->vmcs); + + if (!memcmp(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs))) + tst_res(TFAIL, "VMWRITE did not modify raw VMCS data"); + + /* Run nested VM */ + memcpy(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs)); + kvm_vmx_vmrun(vcpu); + val = kvm_vmx_vmread(VMX_VMCS_EXIT_REASON); + + if (val != VMX_EXIT_HLT) { + tst_res(TFAIL, "Unexpected guest exit reason %llx", val); + return; + } + + if (vcpu->regs.rax == GUEST_READ_ERROR) { + tst_res(TFAIL, "Guest failed to read shadow VMCS"); + return; + } + + if (vcpu->regs.rax == GUEST_WRITE_ERROR) { + tst_res(TFAIL, "Guest failed to write shadow VMCS"); + return; + } + + if (!memcmp(vmcs_backup, shadow_vmcs, sizeof(struct kvm_vmcs))) + tst_res(TFAIL, "Nested VMWRITE did not modify raw VMCS data"); + + /* Check values read by the nested VM from shadow VMCS */ + for (i = 0, errors = 0; i < SHADOW_DATA_LENGTH; i++) { + if (vmread_buffer[i] == host_data[i].value) + continue; + + errors++; + tst_res(TFAIL, "Shadow %s guest mismatch: %lx != %lx", + host_data[i].name, vmread_buffer[i], + host_data[i].value); + } + + if (!errors) + tst_res(TPASS, "Guest read correct values from shadow VMCS"); + + /* Check values written by the nested VM to shadow VMCS */ + kvm_vmx_vmptrld(shadow_vmcs); + + for (i = 0, errors = 0; i < SHADOW_DATA_LENGTH; i++) { + val = kvm_vmx_vmread(guest_data[i].field_id); + + if (val == guest_data[i].value) + continue; + + errors++; + tst_res(TFAIL, "Shadow %s parent mismatch: %llx != %lx", + guest_data[i].name, val, guest_data[i].value); + } + + if (!errors) + tst_res(TPASS, "Parent read correct values from shadow VMCS"); +} + +#else /* defined(__i386__) || defined(__x86_64__) */ +TST_TEST_TCONF("Test supported only on x86"); +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#else /* COMPILE_PAYLOAD */ + +#include "tst_module.h" + +#define NESTED_INTEL_SYSFILE "/sys/module/kvm_intel/parameters/nested" + +static void setup(void) +{ + if (!tst_read_bool_sys_param(NESTED_INTEL_SYSFILE)) { + tst_module_reload("kvm_intel", + (char *const[]){"nested=1", NULL}); + } + + tst_kvm_setup(); +} + +static struct tst_test test = { + .test_all = tst_kvm_run, + .setup = setup, + .cleanup = tst_kvm_cleanup, + .needs_root = 1, + .supported_archs = (const char *const []) { + "x86_64", + "x86", + NULL + }, +}; + +#endif /* COMPILE_PAYLOAD */ diff --git a/testcases/kernel/kvm/kvm_vmx02.c b/testcases/kernel/kvm/kvm_vmx02.c new file mode 100644 index 00000000..3fcfebb3 --- /dev/null +++ b/testcases/kernel/kvm/kvm_vmx02.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC + */ + +/*\ + * Verify that reads and writes to virtualized APIC in nested VM get + * redirected to the memory page selected by parent VM. + */ + +#include "kvm_test.h" + +#ifdef COMPILE_PAYLOAD +#if defined(__i386__) || defined(__x86_64__) + +#include "kvm_x86_vmx.h" + +#define TPR_OFFSET 0x80 +#define TPR_VALUE 0x7 +#define TPR_OLD_VALUE 0x9 +#define MSR_TPR 0x808 + +static void *apic_base; + +int memapic_guest_main(void) +{ + int ret; + uint32_t *ptr; + + ptr = apic_base + TPR_OFFSET; + ret = *ptr; + *ptr = TPR_VALUE; + return ret; +} + +int msrapic_guest_main(void) +{ + int ret; + + ret = kvm_rdmsr(MSR_TPR); + kvm_wrmsr(MSR_TPR, TPR_VALUE); + return ret; +} + +int setup_vmcs(void *apic_ptr, void *msr_mask, uint64_t ec2_flags) +{ + uint32_t *tpr; + uint64_t val, mask, flags; + + /* Check secondary VMCS execctl support */ + mask = kvm_vmx_read_vmctl_mask(VMX_CTLMASK_EXECCTL); + + if (!((mask >> 32) & VMX_EXECCTL_ENABLE_CTL2)) + return 0; + + /* Create and configure guest VMCS */ + tpr = apic_ptr + TPR_OFFSET; + memset(apic_ptr, 0xa9, PAGESIZE); + memset(apic_base, 0xab, PAGESIZE); + *tpr = TPR_OLD_VALUE; + val = kvm_vmx_vmread(VMX_VMCS_VMEXEC_CTL); + flags = VMX_EXECCTL_TPR_SHADOW | VMX_EXECCTL_ENABLE_CTL2; + flags |= VMX_EXECCTL_MSR_BITMAP; + flags &= mask >> 32; + kvm_vmx_vmwrite(VMX_VMCS_VMEXEC_CTL, val | flags); + + if (flags & VMX_EXECCTL_MSR_BITMAP) { + kvm_vmx_vmwrite(VMX_VMCS_MSR_BITMAP_POINTER, + (uintptr_t)msr_mask); + } + + mask = kvm_vmx_read_vmctl_mask(VMX_CTLMASK_EXECCTL2); + ec2_flags &= mask >> 32; + + if (!ec2_flags) + return 0; + + val = ec2_flags | (uint32_t)mask; + kvm_vmx_vmwrite(VMX_VMCS_VMEXEC_CTL2, val); + kvm_vmx_vmwrite(VMX_VMCS_VIRT_APIC_POINTER, (uintptr_t)apic_ptr); + kvm_vmx_vmwrite(VMX_VMCS_VIRT_APIC_BASE, (uintptr_t)apic_base); + return 1; +} + +void check_result(struct kvm_vmx_vcpu *vcpu, unsigned int tpr, + unsigned int tpr_b) +{ + /* Cast RAX value to int. The upper 32 bits may contain garbage. */ + int tpr_orig = vcpu->regs.rax; + + if (tpr_orig == TPR_OLD_VALUE) + tst_res(TPASS, "vTPR has correct value"); + else + tst_res(TFAIL, "Unexpected vTPR value: %x", tpr_orig); + + if (tpr == TPR_VALUE) + tst_res(TPASS, "vAPIC write was handled correctly"); + else if (tpr_b == TPR_VALUE) + tst_res(TFAIL, "vAPIC write was stored to literal address"); + else + tst_res(TFAIL, "vAPIC write may have overwritten host memory!"); +} + +void main(void) +{ + void *apic_ptr, *msr_mask; + struct kvm_vmx_vcpu *vcpu; + uintptr_t rsp; + uint32_t *tpr, *tpr_b; + uint64_t reason; + uint16_t ss; + + kvm_set_vmx_state(1); + + apic_ptr = tst_heap_alloc_aligned(2 * PAGESIZE, PAGESIZE); + apic_base = apic_ptr + PAGESIZE; + tpr = apic_ptr + TPR_OFFSET; + tpr_b = apic_base + TPR_OFFSET; + msr_mask = tst_heap_alloc_aligned(PAGESIZE, PAGESIZE); + memset(msr_mask, 0, PAGESIZE); + vcpu = kvm_create_vmx_vcpu(memapic_guest_main, 1); + kvm_vmx_vmptrld(vcpu->vmcs); + ss = kvm_vmx_vmread(VMX_VMCS_GUEST_SS) >> 3; + rsp = kvm_vmx_vmread(VMX_VMCS_GUEST_RSP); + tst_res(TINFO, "Testing memory-mapped APIC"); + + if (setup_vmcs(apic_ptr, msr_mask, VMX_EXECCTL2_VIRT_APIC)) { + kvm_vmx_vmrun(vcpu); + reason = kvm_vmx_vmread(VMX_VMCS_EXIT_REASON); + + if (reason != VMX_EXIT_HLT) { + tst_res(TFAIL, "Unexpected guest exit reason %llx", + reason); + } else { + check_result(vcpu, *tpr, *tpr_b); + } + } else { + tst_res(TCONF, "CPU does not support memory mapped vAPIC"); + } + + tst_res(TINFO, "Testing MSR-based APIC"); + kvm_init_vmx_vcpu(vcpu, ss, (void *)rsp, msrapic_guest_main); + + if (setup_vmcs(apic_ptr, msr_mask, VMX_EXECCTL2_VIRT_X2APIC)) { + kvm_vmx_vmrun(vcpu); + reason = kvm_vmx_vmread(VMX_VMCS_EXIT_REASON); + + if (reason == VMX_EXIT_RDMSR) { + tst_res(TCONF, "CPU does not support MSR bitmaps"); + } else if (reason != VMX_EXIT_HLT) { + tst_res(TFAIL, "Unexpected guest exit reason %llx", + reason); + } else { + check_result(vcpu, *tpr, *tpr_b); + } + } else { + tst_res(TCONF, "CPU does not support MSR-based vAPIC"); + } +} + +#else /* defined(__i386__) || defined(__x86_64__) */ +TST_TEST_TCONF("Test supported only on x86"); +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#else /* COMPILE_PAYLOAD */ + +#include "tst_module.h" + +#define NESTED_INTEL_SYSFILE "/sys/module/kvm_intel/parameters/nested" + +static void setup(void) +{ + if (!tst_read_bool_sys_param(NESTED_INTEL_SYSFILE)) { + tst_module_reload("kvm_intel", + (char *const[]){"nested=1", NULL}); + } + + tst_kvm_setup(); +} + +static struct tst_test test = { + .test_all = tst_kvm_run, + .setup = setup, + .cleanup = tst_kvm_cleanup, + .needs_root = 1, + .supported_archs = (const char *const []) { + "x86_64", + "x86", + NULL + }, +}; + +#endif /* COMPILE_PAYLOAD */ diff --git a/testcases/kernel/kvm/lib_guest.c b/testcases/kernel/kvm/lib_guest.c index f3e21d3d..6f0b2824 100644 --- a/testcases/kernel/kvm/lib_guest.c +++ b/testcases/kernel/kvm/lib_guest.c @@ -45,6 +45,18 @@ void *memcpy(void *dest, const void *src, size_t size) return dest; } +int memcmp(const void *a, const void *b, size_t length) +{ + const unsigned char *x = a, *y = b; + + for (; length; x++, y++, length--) { + if (*x != *y) + return (int)*x - (int)*y; + } + + return 0; +} + char *strcpy(char *dest, const char *src) { char *ret = dest; @@ -76,6 +88,74 @@ size_t strlen(const char *str) return ret; } +char *strchr(const char *s, int c) +{ + for (; *s; s++) { + if (*s == c) + return (char *)s; + } + + return NULL; +} + +char *strrchr(const char *s, int c) +{ + const char *ret = NULL; + + for (; *s; s++) { + if (*s == c) + ret = s; + } + + return (char *)ret; +} + +#if defined(__x86_64__) && !defined(__ILP32__) +uint64_t u64divu16(uint64_t a, uint16_t b) +{ + return a / b; +} + +unsigned int u64modu16(uint64_t a, uint16_t b) +{ + return a % b; +} + +#else /* defined(__x86_64__) && !defined(__ILP32__) */ + +/* u64 short division helpers to avoid need to link libgcc on 32bit archs */ +uint64_t u64divu16(uint64_t a, uint16_t b) +{ + uint64_t ret = 0; + uint32_t tmp = a >> 32; + + ret = tmp / b; + ret <<= 32; + tmp %= b; + tmp <<= 16; + tmp |= (a >> 16) & 0xffff; + ret |= (tmp / b) << 16; + tmp %= b; + tmp <<= 16; + tmp |= a & 0xffff; + ret |= tmp / b; + return ret; +} + +unsigned int u64modu16(uint64_t a, uint16_t b) +{ + uint32_t tmp = a >> 32; + + tmp %= b; + tmp <<= 16; + tmp |= (a >> 16) & 0xffff; + tmp %= b; + tmp <<= 16; + tmp |= a & 0xffff; + return tmp % b; +} +#endif /* defined(__x86_64__) && !defined(__ILP32__) */ + char *ptr2hex(char *dest, uintptr_t val) { unsigned int i; @@ -95,6 +175,250 @@ char *ptr2hex(char *dest, uintptr_t val) return ret; } +char *u64tostr(char *dest, uint64_t val, uint16_t base, int caps) +{ + unsigned int i; + uintptr_t tmp = u64divu16(val, base); + char hex = caps ? 'A' : 'a'; + char *ret = dest; + + for (i = 1; tmp; i++, tmp = u64divu16(tmp, base)) + ; + + dest[i] = '\0'; + + do { + tmp = u64modu16(val, base); + dest[--i] = tmp + (tmp >= 10 ? hex - 10 : '0'); + val = u64divu16(val, base); + } while (i); + + return ret; +} + +char *i64tostr(char *dest, int64_t val) +{ + if (val < 0) { + dest[0] = '-'; + u64tostr(dest + 1, -val, 10, 0); + return dest; + } + + return u64tostr(dest, val, 10, 0); +} + +int vsprintf(char *dest, const char *fmt, va_list ap) +{ + va_list args; + int ret = 0; + char conv; + uint64_t u64val = 0; + int64_t i64val = 0; + const char * const uint_conv = "ouxX"; + + va_copy(args, ap); + + for (; *fmt; fmt++) { + if (*fmt != '%') { + dest[ret++] = *fmt; + continue; + } + + conv = 0; + fmt++; + + switch (*fmt) { + case '%': + dest[ret++] = *fmt; + break; + + case 'c': + dest[ret++] = va_arg(args, int); + break; + + case 's': + strcpy(dest + ret, va_arg(args, const char *)); + ret += strlen(dest + ret); + break; + + case 'p': + strcpy(dest + ret, "0x"); + ptr2hex(dest + ret + 2, + (uintptr_t)va_arg(args, void *)); + ret += strlen(dest + ret); + break; + + case 'l': + fmt++; + + switch (*fmt) { + case 'l': + fmt++; + + if (*fmt == 'd' || *fmt == 'i') { + i64val = va_arg(args, long long); + conv = *fmt; + break; + } + + if (strchr(uint_conv, *fmt)) { + u64val = va_arg(args, + unsigned long long); + conv = *fmt; + break; + } + + va_end(args); + return -1; + + case 'd': + case 'i': + i64val = va_arg(args, long); + conv = *fmt; + break; + + default: + if (strchr(uint_conv, *fmt)) { + u64val = va_arg(args, + unsigned long); + conv = *fmt; + break; + } + + va_end(args); + return -1; + } + break; + + case 'h': + fmt++; + + switch (*fmt) { + case 'h': + fmt++; + + if (*fmt == 'd' || *fmt == 'i') { + i64val = (signed char)va_arg(args, int); + conv = *fmt; + break; + } + + if (strchr(uint_conv, *fmt)) { + u64val = (unsigned char)va_arg(args, + unsigned int); + conv = *fmt; + break; + } + + va_end(args); + return -1; + + case 'd': + case 'i': + i64val = (short int)va_arg(args, int); + conv = *fmt; + break; + + default: + if (strchr(uint_conv, *fmt)) { + u64val = (unsigned short int)va_arg( + args, unsigned int); + conv = *fmt; + break; + } + + va_end(args); + return -1; + } + break; + + case 'z': + fmt++; + + if (*fmt == 'd' || *fmt == 'i') { + i64val = va_arg(args, ssize_t); + conv = *fmt; + break; + } + + if (strchr(uint_conv, *fmt)) { + u64val = va_arg(args, size_t); + conv = *fmt; + break; + } + + va_end(args); + return -1; + + case 'd': + case 'i': + i64val = va_arg(args, int); + conv = *fmt; + break; + + default: + if (strchr(uint_conv, *fmt)) { + u64val = va_arg(args, unsigned int); + conv = *fmt; + break; + } + + va_end(args); + return -1; + } + + switch (conv) { + case 0: + continue; + + case 'd': + case 'i': + i64tostr(dest + ret, i64val); + ret += strlen(dest + ret); + break; + + case 'o': + u64tostr(dest + ret, u64val, 8, 0); + ret += strlen(dest + ret); + break; + + case 'u': + u64tostr(dest + ret, u64val, 10, 0); + ret += strlen(dest + ret); + break; + + case 'x': + u64tostr(dest + ret, u64val, 16, 0); + ret += strlen(dest + ret); + break; + + case 'X': + u64tostr(dest + ret, u64val, 16, 1); + ret += strlen(dest + ret); + break; + + default: + va_end(args); + return -1; + } + } + + va_end(args); + dest[ret++] = '\0'; + return ret; +} + +int sprintf(char *dest, const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = vsprintf(dest, fmt, args); + va_end(args); + return ret; +} + void *tst_heap_alloc_aligned(size_t size, size_t align) { uintptr_t addr = (uintptr_t)heap_end; @@ -131,6 +455,7 @@ static void tst_fatal_error(const char *file, const int lineno, test_result->result = TBROK; test_result->lineno = lineno; test_result->file_addr = (uintptr_t)file; + /* Avoid sprintf() here in case of bugs */ strcpy(test_result->message, message); strcat(test_result->message, " at address 0x"); ptr2hex(test_result->message + strlen(test_result->message), ip); @@ -139,19 +464,46 @@ static void tst_fatal_error(const char *file, const int lineno, } void tst_res_(const char *file, const int lineno, int result, - const char *message) + const char *fmt, ...) { + va_list args; + int ret; + + va_start(args, fmt); test_result->result = result; test_result->lineno = lineno; test_result->file_addr = (uintptr_t)file; - strcpy(test_result->message, message); + ret = vsprintf(test_result->message, fmt, args); + va_end(args); + + if (ret < 0) { + tst_brk_(file, lineno, TBROK, "Invalid tst_res() format: %s", + fmt); + } + kvm_yield(); } void tst_brk_(const char *file, const int lineno, int result, - const char *message) + const char *fmt, ...) { - tst_res_(file, lineno, result, message); + va_list args; + int ret; + + va_start(args, fmt); + test_result->result = result; + test_result->lineno = lineno; + test_result->file_addr = (uintptr_t)file; + ret = vsprintf(test_result->message, fmt, args); + va_end(args); + + if (ret < 0) { + test_result->result = TBROK; + strcpy(test_result->message, "Invalid tst_brk() format: "); + strcat(test_result->message, fmt); + } + + kvm_yield(); kvm_exit(); } diff --git a/testcases/kernel/kvm/lib_x86.c b/testcases/kernel/kvm/lib_x86.c index 3e6656f1..c3363e03 100644 --- a/testcases/kernel/kvm/lib_x86.c +++ b/testcases/kernel/kvm/lib_x86.c @@ -6,6 +6,9 @@ */ #include "kvm_x86_svm.h" +#include "kvm_x86_vmx.h" + +#define VMX_VMINST_ERR_COUNT 29 void kvm_svm_guest_entry(void); @@ -84,6 +87,56 @@ static uintptr_t intr_handlers[] = { 0 }; +static const char *vmx_error_description[VMX_VMINST_ERR_COUNT] = { + "Success", + "VMCALL executed in VMX root", + "VMCLEAR on invalid pointer", + "VMCLEAR on VMXON pointer", + "VMLAUNCH with non-clear VMCS", + "VMRESUME with non-launched VMCS", + "VMRESUME after VMXOFF", + "VM entry with invalid VMCS control fields", + "VM entry with invalid VMCS host state", + "VMPTRLD with invalid pointer", + "VMPTRLD with VMXON pointer", + "VMPTRLD with incorrect VMCS version field", + "Invalid VMCS field code", + "VMWRITE to read-only VMCS field", + "Unknown error", + "VMXON called twice", + "VM entry with invalid executive VMCS pointer", + "VM entry with non-launched executive VMCS", + "VM entry with executive VMCS pointer != VMXON pointer", + "VMCALL with non-clear VMCS", + "VMCALL with invalid VMCS exit control fields", + "Unknown error", + "VMCALL with incorrect MSEG revision ID", + "VMXOFF under dual-monitor SMIs and SMM", + "VMCALL with invalid SMM-monitor features", + "VM entry with invalid executive VMCS execution control fields", + "VM entry with events blocked by MOV SS", + "Unknown error", + "Invalid operand to INVEPT/INVVPID" +}; + +static const unsigned int vmx_ctl_masks_old[VMX_CTLMASK_MAX] = { + MSR_IA32_VMX_PINX_MASK, + MSR_IA32_VMX_EXECCTL_MASK, + MSR_IA32_VMX_EXECCTL2_MASK, + MSR_IA32_VMX_EXECCTL3_MASK, + MSR_IA32_VMX_EXITCTL_MASK, + MSR_IA32_VMX_ENTRYCTL_MASK +}; + +static const unsigned int vmx_ctl_masks_new[VMX_CTLMASK_MAX] = { + MSR_IA32_VMX_PINX_MASK2, + MSR_IA32_VMX_EXECCTL_MASK2, + MSR_IA32_VMX_EXECCTL2_MASK, + MSR_IA32_VMX_EXECCTL3_MASK, + MSR_IA32_VMX_EXITCTL_MASK2, + MSR_IA32_VMX_ENTRYCTL_MASK2 +}; + static void kvm_set_intr_handler(unsigned int id, uintptr_t func) { memset(kvm_idt + id, 0, sizeof(kvm_idt[0])); @@ -174,7 +227,7 @@ int kvm_find_free_descriptor(const struct segment_descriptor *table, const struct segment_descriptor *ptr; size_t i; - for (i = 0, ptr = table; i < size; i++, ptr++) { + for (i = 1, ptr = table + 1; i < size; i++, ptr++) { if (!(ptr->flags_lo & SEGFLAG_PRESENT)) return i; @@ -214,6 +267,33 @@ void kvm_get_cpuid(unsigned int eax, unsigned int ecx, struct kvm_cpuid *buf) ); } +void kvm_set_cr0(unsigned long val) +{ + asm ( + "mov %0, %%cr0\n" + : + : "r" (val) + ); +} + +void kvm_set_cr3(unsigned long val) +{ + asm ( + "mov %0, %%cr3\n" + : + : "r" (val) + ); +} + +void kvm_set_cr4(unsigned long val) +{ + asm ( + "mov %0, %%cr4\n" + : + : "r" (val) + ); +} + uint64_t kvm_rdmsr(unsigned int msr) { unsigned int ret_lo, ret_hi; @@ -393,3 +473,482 @@ struct kvm_svm_vcpu *kvm_create_svm_vcpu(int (*guest_main)(void), ret->vmcb = vmcb; return ret; } + +void kvm_svm_vmload(struct kvm_vmcb *buf) +{ + asm ( + "vmload %0\n" + : + : "a" (buf) + ); +} + +void kvm_svm_vmsave(struct kvm_vmcb *buf) +{ + asm ( + "vmsave %0\n" + : + : "a" (buf) + ); +} + +int kvm_is_vmx_supported(void) +{ + struct kvm_cpuid buf; + + kvm_get_cpuid(CPUID_GET_MODEL_INFO, 0, &buf); + return buf.ecx & CPUID_MODEL_VMX; +} + +void kvm_vmx_vmclear(struct kvm_vmcs *buf) +{ + uint64_t tmp = (uintptr_t)buf; + + asm goto( + "vmclear (%0)\n" + "jna %l[error]\n" + : + : "r" (&tmp) + : "cc", "memory" + : error + ); + + return; + +error: + tst_brk(TBROK, "VMCLEAR(%p) failed", buf); +} + +void kvm_vmx_vmptrld(struct kvm_vmcs *buf) +{ + uint64_t tmp = (uintptr_t)buf; + + asm goto( + "vmptrld (%0)\n" + "jna %l[error]\n" + : + : "r" (&tmp) + : "cc" + : error + ); + + return; + +error: + tst_brk(TBROK, "VMPTRLD(%p) failed", buf); +} + +uint64_t kvm_vmx_vmptrst(void) +{ + uint64_t ret; + + asm ( + "vmptrst (%0)\n" + : + : "r" (&ret) + : "cc", "memory" + ); + + return ret; +} + +uint64_t kvm_vmx_vmread(unsigned long var_id) +{ + uint64_t ret = 0; + unsigned long tmp; + +#ifndef __x86_64__ + if ((var_id & VMX_VMCSFIELD_SIZE_MASK) == VMX_VMCSFIELD_64BIT) { + asm goto( + "vmread %1, (%0)\n" + "jna %l[error]\n" + : + : "r" (&tmp), "r" (var_id + 1) + : "cc", "memory" + : error + ); + + ret = tmp; + ret <<= 32; + } +#endif /* __x86_64__ */ + + asm goto( + "vmread %1, (%0)\n" + "jna %l[error]\n" + : + : "r" (&tmp), "r" (var_id) + : "cc", "memory" + : error + ); + + ret |= tmp; + return ret; + +error: + tst_brk(TBROK, "VMREAD(%lx) failed", var_id); +} + +void kvm_vmx_vmwrite(unsigned long var_id, uint64_t value) +{ + unsigned long tmp = value; + + asm goto( + "vmwrite %0, %1\n" + "jna %l[error]\n" + : + : "r" (tmp), "r" (var_id) + : "cc" + : error + ); + +#ifndef __x86_64__ + if ((var_id & VMX_VMCSFIELD_SIZE_MASK) == VMX_VMCSFIELD_64BIT) { + tmp = value >> 32; + + asm goto( + "vmwrite %0, %1\n" + "jna %l[error]\n" + : + : "r" (tmp), "r" (var_id + 1) + : "cc" + : error + ); + + } +#endif /* __x86_64__ */ + + return; + +error: + tst_brk(TBROK, "VMWRITE(%lx, %llx) failed", var_id, value); +} + +static void kvm_vmx_vmxon(struct kvm_vmcs *buf) +{ + uint64_t tmp = (uintptr_t)buf; + + asm goto( + "vmxon (%0)\n" + "jna %l[error]\n" + : + : "r" (&tmp) + : "cc" + : error + ); + + return; + +error: + tst_brk(TBROK, "VMXON(%p) failed", buf); +} + +static void kvm_vmx_vmxoff(void) +{ + asm goto( + "vmxoff\n" + "jna %l[error]\n" + : + : + : "cc" + : error + ); + + return; + +error: + tst_brk(TBROK, "VMXOFF failed"); +} + +struct kvm_vmcs *kvm_alloc_vmcs(void) +{ + struct kvm_vmcs *ret; + + ret = tst_heap_alloc_aligned(sizeof(struct kvm_vmcs), PAGESIZE); + memset(ret, 0, sizeof(struct kvm_vmcs)); + ret->version = (uint32_t)kvm_rdmsr(MSR_IA32_VMX_BASIC); + return ret; +} + +void kvm_set_vmx_state(int enabled) +{ + static struct kvm_vmcs *vmm_buf; + uint64_t value; + struct kvm_cregs cregs; + + if (!kvm_is_vmx_supported()) + tst_brk(TCONF, "CPU does not support VMX"); + + kvm_read_cregs(&cregs); + kvm_set_cr0(cregs.cr0 | CR0_NE); + kvm_set_cr4(cregs.cr4 | CR4_VMXE); + value = kvm_rdmsr(MSR_IA32_FEATURE_CONTROL); + value |= IA32FC_LOCK | IA32FC_VMXON_NORMAL; + kvm_wrmsr(MSR_IA32_FEATURE_CONTROL, value); + + if (!vmm_buf) + vmm_buf = kvm_alloc_vmcs(); + + if (enabled) + kvm_vmx_vmxon(vmm_buf); + else + kvm_vmx_vmxoff(); +} + +void kvm_vmcs_copy_gdt_descriptor(unsigned int gdt_id, + unsigned long vmcs_selector, unsigned long vmcs_flags, + unsigned long vmcs_limit, unsigned long vmcs_baseaddr) +{ + uint64_t baseaddr; + uint32_t limit; + unsigned int flags; + + if (gdt_id >= KVM_GDT_SIZE) + tst_brk(TBROK, "GDT descriptor ID out of range"); + + kvm_parse_segment_descriptor(kvm_gdt + gdt_id, &baseaddr, &limit, + &flags); + + if (!(flags & SEGFLAG_PRESENT)) { + gdt_id = 0; + baseaddr = 0; + flags = 0x10000; + limit = 0; + } else if (flags & SEGFLAG_PAGE_LIMIT) { + limit = (limit << 12) | 0xfff; + } + + if (!(flags & 0x10000)) { + // insert the reserved limit bits and force accessed bit to 1 + flags = ((flags & 0xf00) << 4) | (flags & 0xff) | 0x1; + } + + kvm_vmx_vmwrite(vmcs_selector, gdt_id << 3); + kvm_vmx_vmwrite(vmcs_flags, flags); + kvm_vmx_vmwrite(vmcs_limit, limit); + kvm_vmx_vmwrite(vmcs_baseaddr, baseaddr); +} + +uint64_t kvm_vmx_read_vmctl_mask(unsigned int ctl_id) +{ + unsigned int msr; + + if (ctl_id >= VMX_CTLMASK_MAX) + tst_brk(TBROK, "Invalid VMX control ID %u", ctl_id); + + if (kvm_rdmsr(MSR_IA32_VMX_BASIC) & IA32_VMXBASIC_USELESS_CTL_MASKS) + msr = vmx_ctl_masks_new[ctl_id]; + else + msr = vmx_ctl_masks_old[ctl_id]; + + return kvm_rdmsr(msr); +} + +void kvm_init_vmx_vcpu(struct kvm_vmx_vcpu *cpu, uint16_t ss, void *rsp, + int (*guest_main)(void)) +{ + uint64_t old_vmcs, pinxctl, execctl, entryctl, exitctl; + unsigned long crx; + struct kvm_cregs cregs; + struct kvm_sregs sregs; + + kvm_read_cregs(&cregs); + kvm_read_sregs(&sregs); + + /* Clear cpu->vmcs first in case it's the current VMCS */ + kvm_vmx_vmclear(cpu->vmcs); + memset(&cpu->regs, 0, sizeof(struct kvm_regs64)); + cpu->launched = 0; + old_vmcs = kvm_vmx_vmptrst(); + kvm_vmx_vmptrld(cpu->vmcs); + + /* Configure VM execution control fields */ + pinxctl = (uint32_t)kvm_vmx_read_vmctl_mask(VMX_CTLMASK_PINX); + execctl = (uint32_t)kvm_vmx_read_vmctl_mask(VMX_CTLMASK_EXECCTL); + exitctl = (uint32_t)kvm_vmx_read_vmctl_mask(VMX_CTLMASK_EXITCTL); + entryctl = (uint32_t)kvm_vmx_read_vmctl_mask(VMX_CTLMASK_ENTRYCTL); + + execctl |= VMX_INTERCEPT_HLT; + + if (kvm_rdmsr(MSR_EFER) & EFER_LME) { + entryctl |= VMX_ENTRYCTL_X64; + exitctl |= VMX_EXITCTL_X64; + } + + kvm_vmx_vmwrite(VMX_VMCS_VMPINX_CTL, pinxctl); + kvm_vmx_vmwrite(VMX_VMCS_VMEXEC_CTL, execctl); + kvm_vmx_vmwrite(VMX_VMCS_VMENTRY_CTL, entryctl); + kvm_vmx_vmwrite(VMX_VMCS_VMEXIT_CTL, exitctl); + kvm_vmx_vmwrite(VMX_VMCS_LINK_POINTER, VMX_INVALID_VMCS); + kvm_vmcs_copy_gdt_descriptor(sregs.es >> 3, VMX_VMCS_GUEST_ES, + VMX_VMCS_GUEST_ES_ACCESS, VMX_VMCS_GUEST_ES_LIMIT, + VMX_VMCS_GUEST_ES_BASE); + kvm_vmcs_copy_gdt_descriptor(sregs.cs >> 3, VMX_VMCS_GUEST_CS, + VMX_VMCS_GUEST_CS_ACCESS, VMX_VMCS_GUEST_CS_LIMIT, + VMX_VMCS_GUEST_CS_BASE); + kvm_vmcs_copy_gdt_descriptor(ss, VMX_VMCS_GUEST_SS, + VMX_VMCS_GUEST_SS_ACCESS, VMX_VMCS_GUEST_SS_LIMIT, + VMX_VMCS_GUEST_SS_BASE); + kvm_vmcs_copy_gdt_descriptor(sregs.ds >> 3, VMX_VMCS_GUEST_DS, + VMX_VMCS_GUEST_DS_ACCESS, VMX_VMCS_GUEST_DS_LIMIT, + VMX_VMCS_GUEST_DS_BASE); + kvm_vmcs_copy_gdt_descriptor(sregs.fs >> 3, VMX_VMCS_GUEST_FS, + VMX_VMCS_GUEST_FS_ACCESS, VMX_VMCS_GUEST_FS_LIMIT, + VMX_VMCS_GUEST_FS_BASE); + kvm_vmcs_copy_gdt_descriptor(sregs.gs >> 3, VMX_VMCS_GUEST_GS, + VMX_VMCS_GUEST_GS_ACCESS, VMX_VMCS_GUEST_GS_LIMIT, + VMX_VMCS_GUEST_GS_BASE); + kvm_vmcs_copy_gdt_descriptor(sregs.tr >> 3, VMX_VMCS_GUEST_TR, + VMX_VMCS_GUEST_TR_ACCESS, VMX_VMCS_GUEST_TR_LIMIT, + VMX_VMCS_GUEST_TR_BASE); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_LDTR, 0); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_LDTR_ACCESS, 0x10000); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_LDTR_LIMIT, 0); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_LDTR_BASE, 0); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_GDTR_BASE, (uintptr_t)kvm_gdt); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_GDTR_LIMIT, + (KVM_GDT_SIZE * sizeof(struct segment_descriptor)) - 1); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_IDTR_BASE, (uintptr_t)kvm_idt); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_IDTR_LIMIT, + (X86_INTR_COUNT * sizeof(struct intr_descriptor)) - 1); + + crx = cregs.cr0 & kvm_rdmsr(MSR_IA32_VMX_CR0_FIXED1); + crx |= kvm_rdmsr(MSR_IA32_VMX_CR0_FIXED0); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_CR0, crx); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_CR3, cregs.cr3); + crx = cregs.cr4 & kvm_rdmsr(MSR_IA32_VMX_CR4_FIXED1); + crx |= kvm_rdmsr(MSR_IA32_VMX_CR4_FIXED0); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_CR4, crx); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_RSP, (uintptr_t)rsp); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_RIP, (uintptr_t)kvm_svm_guest_entry); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_RFLAGS, 0x202); /* Interrupts enabled */ + kvm_vmx_vmwrite(VMX_VMCS_GUEST_SYSENTER_ESP, 0); + kvm_vmx_vmwrite(VMX_VMCS_GUEST_SYSENTER_EIP, 0); + cpu->regs.rax = (uintptr_t)guest_main; + + /* Reactivate previous VMCS (if any) */ + if (old_vmcs != VMX_INVALID_VMCS) + kvm_vmx_vmptrld((struct kvm_vmcs *)(uintptr_t)old_vmcs); +} + +struct kvm_vmx_vcpu *kvm_create_vmx_vcpu(int (*guest_main)(void), + int alloc_stack) +{ + uint16_t ss = 0; + char *stack = NULL; + struct kvm_vmcs *vmcs; + struct kvm_vmx_vcpu *ret; + + vmcs = kvm_alloc_vmcs(); + + if (alloc_stack) { + stack = tst_heap_alloc_aligned(2 * PAGESIZE, PAGESIZE); + ss = kvm_create_stack_descriptor(kvm_gdt, KVM_GDT_SIZE, stack); + stack += 2 * PAGESIZE; + } + + ret = tst_heap_alloc(sizeof(struct kvm_vmx_vcpu)); + memset(ret, 0, sizeof(struct kvm_vmx_vcpu)); + ret->vmcs = vmcs; + kvm_init_vmx_vcpu(ret, ss, stack, guest_main); + return ret; +} + +void kvm_vmx_activate_vcpu(struct kvm_vmx_vcpu *cpu) +{ + struct kvm_cregs cregs; + struct kvm_sregs sregs; + uint64_t baseaddr; + + kvm_read_cregs(&cregs); + kvm_read_sregs(&sregs); + + kvm_vmx_vmptrld(cpu->vmcs); + kvm_vmx_vmwrite(VMX_VMCS_HOST_ES, sregs.es); + kvm_vmx_vmwrite(VMX_VMCS_HOST_CS, sregs.cs); + kvm_vmx_vmwrite(VMX_VMCS_HOST_SS, sregs.ss); + kvm_vmx_vmwrite(VMX_VMCS_HOST_DS, sregs.ds); + kvm_vmx_vmwrite(VMX_VMCS_HOST_FS, sregs.fs); + kvm_vmx_vmwrite(VMX_VMCS_HOST_GS, sregs.gs); + kvm_vmx_vmwrite(VMX_VMCS_HOST_TR, sregs.tr); + + kvm_vmx_vmwrite(VMX_VMCS_HOST_CR0, cregs.cr0); + kvm_vmx_vmwrite(VMX_VMCS_HOST_CR3, cregs.cr3); + kvm_vmx_vmwrite(VMX_VMCS_HOST_CR4, cregs.cr4); + kvm_parse_segment_descriptor(kvm_gdt + (sregs.fs >> 3), &baseaddr, + NULL, NULL); + kvm_vmx_vmwrite(VMX_VMCS_HOST_FS_BASE, baseaddr); + kvm_parse_segment_descriptor(kvm_gdt + (sregs.gs >> 3), &baseaddr, + NULL, NULL); + kvm_vmx_vmwrite(VMX_VMCS_HOST_GS_BASE, baseaddr); + kvm_parse_segment_descriptor(kvm_gdt + (sregs.tr >> 3), &baseaddr, + NULL, NULL); + kvm_vmx_vmwrite(VMX_VMCS_HOST_TR_BASE, baseaddr); + kvm_vmx_vmwrite(VMX_VMCS_HOST_GDTR_BASE, (uintptr_t)kvm_gdt); + kvm_vmx_vmwrite(VMX_VMCS_HOST_IDTR_BASE, (uintptr_t)kvm_idt); +} + +void kvm_vmx_vmrun(struct kvm_vmx_vcpu *cpu) +{ + int ret, err; + uint64_t reason; + + kvm_vmx_activate_vcpu(cpu); + + if (cpu->launched) { + ret = kvm_vmx_vmresume(cpu); + } else { + ret = kvm_vmx_vmlaunch(cpu); + cpu->launched = 1; + } + + if (ret) { + err = kvm_vmx_inst_errno(); + tst_brk(TBROK, "VMLAUNCH/VMRESUME failed: %s (%d)", + kvm_vmx_inst_strerr(err), err); + } + + reason = kvm_vmx_vmread(VMX_VMCS_EXIT_REASON); + + if (reason & VMX_EXIT_FAILED_ENTRY) { + tst_brk(TBROK, "VM entry failed. Reason: %llu, qualif.: %llu", + reason & 0xffff, + kvm_vmx_vmread(VMX_VMCS_EXIT_QUALIFICATION)); + } +} + +int kvm_vmx_inst_errno(void) +{ + unsigned long ret, var_id = VMX_VMCS_VMINST_ERROR; + + /* Do not use kvm_vmx_vmread() to avoid tst_brk() on failure */ + asm goto( + "vmread %1, (%0)\n" + "jna %l[error]\n" + : + : "r" (&ret), "r" (var_id) + : "cc", "memory" + : error + ); + + return ret; + +error: + return -1; +} + +const char *kvm_vmx_inst_strerr(int vmx_errno) +{ + if (vmx_errno < 0) + return "Cannot read VM errno - invalid current VMCS?"; + + if (vmx_errno >= VMX_VMINST_ERR_COUNT) + return "Unknown error"; + + return vmx_error_description[vmx_errno]; +} + +const char *kvm_vmx_inst_err(void) +{ + return kvm_vmx_inst_strerr(kvm_vmx_inst_errno()); +} diff --git a/testcases/kernel/lib/numa_cpuset.c b/testcases/kernel/lib/numa_cpuset.c new file mode 100644 index 00000000..088d2d4e --- /dev/null +++ b/testcases/kernel/lib/numa_cpuset.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Linux Test Project, 2011-2021 + * Copyright (c) Cyril Hrubis 2024 + */ + +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" +#include "tst_cgroup.h" +#include "numa_helper.h" + +static void gather_node_cpus(char *cpus, long nd) +{ + int ncpus = 0; + int i; + long online; + char buf[BUFSIZ]; + char path[BUFSIZ], path1[BUFSIZ]; + + while (tst_path_exists(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus)) + ncpus++; + + for (i = 0; i < ncpus; i++) { + snprintf(path, BUFSIZ, + PATH_SYS_SYSTEM "/node/node%ld/cpu%d", nd, i); + if (tst_path_exists("%s", path)) { + snprintf(path1, BUFSIZ, "%s/online", path); + /* + * if there is no online knob, then the cpu cannot + * be taken offline + */ + if (tst_path_exists("%s", path1)) { + SAFE_FILE_SCANF(path1, "%ld", &online); + if (online == 0) + continue; + } + sprintf(buf, "%d,", i); + strcat(cpus, buf); + } + } + /* Remove the trailing comma. */ + cpus[strlen(cpus) - 1] = '\0'; +} + +void write_node_cpusets(const struct tst_cg_group *cg, long nd) +{ + char cpus[BUFSIZ] = ""; + + SAFE_CG_PRINTF(cg, "cpuset.mems", "%ld", nd); + + gather_node_cpus(cpus, nd); + /* + * If the 'nd' node doesn't contain any CPUs, + * the first ID of CPU '0' will be used as + * the value of cpuset.cpus. + */ + if (strlen(cpus) != 0) { + SAFE_CG_PRINT(cg, "cpuset.cpus", cpus); + } else { + tst_res(TINFO, "No CPUs in the node%ld; " + "using only CPU0", nd); + SAFE_CG_PRINT(cg, "cpuset.cpus", "0"); + } +} diff --git a/testcases/kernel/mem/.gitignore b/testcases/kernel/mem/.gitignore index 7258489e..b4455de5 100755 --- a/testcases/kernel/mem/.gitignore +++ b/testcases/kernel/mem/.gitignore @@ -1,4 +1,5 @@ /cpuset/cpuset01 +/cpuset/cpuset02 /hugetlb/hugefallocate/hugefallocate01 /hugetlb/hugefallocate/hugefallocate02 /hugetlb/hugefork/hugefork01 @@ -34,6 +35,7 @@ /hugetlb/hugemmap/hugemmap30 /hugetlb/hugemmap/hugemmap31 /hugetlb/hugemmap/hugemmap32 +/hugetlb/hugemmap/hugemmap34 /hugetlb/hugeshmat/hugeshmat01 /hugetlb/hugeshmat/hugeshmat02 /hugetlb/hugeshmat/hugeshmat03 @@ -47,12 +49,14 @@ /hugetlb/hugeshmget/hugeshmget02 /hugetlb/hugeshmget/hugeshmget03 /hugetlb/hugeshmget/hugeshmget05 +/hugetlb/hugeshmget/hugeshmget06 /ksm/ksm01 /ksm/ksm02 /ksm/ksm03 /ksm/ksm04 /ksm/ksm05 /ksm/ksm06 +/ksm/ksm07 /mem/mem02 /mmapstress/mmap-corruption01 /mmapstress/mmapstress01 @@ -85,7 +89,6 @@ /shmt/shmt03 /shmt/shmt04 /shmt/shmt05 -/shmt/shmt06 /shmt/shmt07 /shmt/shmt08 /shmt/shmt09 diff --git a/testcases/kernel/mem/cpuset/Makefile b/testcases/kernel/mem/cpuset/Makefile index 8e41c022..7010c7be 100755 --- a/testcases/kernel/mem/cpuset/Makefile +++ b/testcases/kernel/mem/cpuset/Makefile @@ -19,6 +19,11 @@ top_srcdir ?= ../../../.. +LTPLIBS = numa + include $(top_srcdir)/include/mk/testcases.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk +include $(top_srcdir)/testcases/kernel/include/lib.mk + +cpuset02: LTPLDLIBS = -lltpnuma + include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/mem/cpuset/cpuset01.c b/testcases/kernel/mem/cpuset/cpuset01.c index 956ac30c..362d645f 100755 --- a/testcases/kernel/mem/cpuset/cpuset01.c +++ b/testcases/kernel/mem/cpuset/cpuset01.c @@ -1,23 +1,18 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later /* * Copyright (C) 2010-2017 Red Hat, Inc. + */ + +/*\ + * Out Of Memory when changing cpuset's mems on NUMA. * - * 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. + * Regression test based on the reproducers posted in: + * https://lore.kernel.org/lkml/4BDFFCC4.5000106@cn.fujitsu.com/ * - * 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. + * Fixed in kernel v2.6.35: * - * Out Of Memory when changing cpuset's mems on NUMA. There was a - * problem reported upstream that the allocator may see an empty - * nodemask when changing cpuset's mems. - * http://lkml.org/lkml/2010/5/4/77 - * http://lkml.org/lkml/2010/5/4/79 - * http://lkml.org/lkml/2010/5/4/80 - * This test is based on the reproducers for the above issue. + * - 708c1bbc9d0c ("mempolicy: restructure rebinding-mempolicy functions") + * - c0ff7453bb5c ("cpuset,mm: fix no node to alloc memory when changing cpuset's mems") */ #include "config.h" @@ -30,7 +25,7 @@ #include #endif -#include "mem.h" +#include "tst_test.h" #include "numa_helper.h" #ifdef HAVE_NUMA_V2 @@ -164,7 +159,7 @@ static long count_cpu(void) { int ncpus = 0; - while (path_exist(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus)) + while (tst_path_exists(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus)) ncpus++; return ncpus; diff --git a/testcases/kernel/mem/cpuset/cpuset02.c b/testcases/kernel/mem/cpuset/cpuset02.c new file mode 100644 index 00000000..ccfddd6a --- /dev/null +++ b/testcases/kernel/mem/cpuset/cpuset02.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (c) 2025 SUSE LLC + */ + +/*\ + * Test checks cpuset.mems works with hugepage file. + * Based on test6 from cpuset_memory_testset.sh written by Miao Xie. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "tst_test.h" + +#ifdef HAVE_NUMA_V2 +#include +#include "tst_numa.h" + +#define MNTPOINT "hugetlbfs/" +#define HUGE_PAGE_FILE MNTPOINT "hugepagefile" + +static long hpage_size; +static struct tst_nodemap *node; +static int check_node_id; +static struct tst_cg_group *cg_cpuset_0; + +static void touch_memory_and_check_node(char *p, int size) +{ + int i; + int node = -1; + long ret; + int pagesize = sysconf(_SC_PAGESIZE); + + for (i = 0; i < size; i += pagesize) + p[i] = 0xef; + + ret = get_mempolicy(&node, NULL, 0, p, MPOL_F_NODE | MPOL_F_ADDR); + if (ret < 0) + tst_brk(TBROK | TERRNO, "get_mempolicy() failed"); + + if (node == check_node_id) + tst_res(TPASS, "1 huge page allocated on node%d as expected", node); + else + tst_res(TFAIL, "1 huge page allocated on node%d unexpected", node); +} + +static void child(void) +{ + char *p; + int fd_hugepage; + + fd_hugepage = SAFE_OPEN(HUGE_PAGE_FILE, O_CREAT | O_RDWR, 0755); + p = SAFE_MMAP(NULL, hpage_size, PROT_WRITE | PROT_READ, + MAP_SHARED, fd_hugepage, 0); + + touch_memory_and_check_node(p, hpage_size); + + SAFE_MUNMAP(p, hpage_size); + SAFE_CLOSE(fd_hugepage); +} + +static void run_test(void) +{ + int pid; + char node_id_str[128]; + + cg_cpuset_0 = tst_cg_group_mk(tst_cg, "0"); + + sprintf(node_id_str, "%u", check_node_id); + SAFE_CG_PRINT(cg_cpuset_0, "cpuset.mems", node_id_str); + + pid = SAFE_FORK(); + + if (!pid) { + SAFE_CG_PRINTF(cg_cpuset_0, "cgroup.procs", "%d", pid); + child(); + return; + } + + SAFE_WAITPID(pid, NULL, 0); + + cg_cpuset_0 = tst_cg_group_rm(cg_cpuset_0); +} + +static void setup(void) +{ + node = tst_get_nodemap(TST_NUMA_MEM, getpagesize() / 1024); + if (node->cnt <= 1) + tst_brk(TCONF, "test requires at least 2 NUMA memory nodes"); + + check_node_id = node->map[node->cnt - 1]; + + hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024; + + char path[128]; + unsigned int i; + unsigned int nr_hugepages; + + for (i = 0; i < node->cnt; i++) { + unsigned int current_node_id = node->map[i]; + + sprintf(path, + "/sys/devices/system/node/node%d/hugepages/hugepages-%ldkB/nr_hugepages", + current_node_id, hpage_size / 1024); + FILE_PRINTF(path, "%d", 1); + SAFE_FILE_SCANF(path, "%d", &nr_hugepages); + if (nr_hugepages != 1) + tst_brk(TCONF, "reserve 1 huge page on node%d failed", current_node_id); + } +} + +static struct tst_test test = { + .needs_root = 1, + .mntpoint = MNTPOINT, + .needs_hugetlbfs = 1, + .setup = setup, + .forks_child = 1, + .test_all = run_test, + .needs_cgroup_ctrls = (const char *const []){ "cpuset", NULL }, +}; + +#else +TST_TEST_TCONF(NUMA_ERROR_MSG); +#endif diff --git a/testcases/kernel/mem/hugetlb/Makefile.inc b/testcases/kernel/mem/hugetlb/Makefile.inc index 9a4aa879..b272e62a 100755 --- a/testcases/kernel/mem/hugetlb/Makefile.inc +++ b/testcases/kernel/mem/hugetlb/Makefile.inc @@ -33,6 +33,3 @@ CPPFLAGS += -I$(abs_srcdir)/$(LIBIPCDIR) LDFLAGS += -L$(abs_builddir)/$(LIBIPCDIR) LDLIBS += -lhugetlb MAKE_DEPS += $(LIBIPC) - -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk -# vim: syntax=make diff --git a/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate01.c b/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate01.c index e4bb21e4..53fef3dd 100644 --- a/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate01.c +++ b/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It tests alignment of fallocate arguments. fallocate will take non-huge * page aligned offsets and addresses. However, operations are only * performed on huge pages. This is different that than fallocate @@ -33,7 +31,7 @@ static void run_test(void) int err; unsigned long free_initial, free_after, free_after_delete; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); free_initial = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE); diff --git a/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate02.c b/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate02.c index 4a25666a..1f7c4dcc 100644 --- a/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate02.c +++ b/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It tests basic fallocate functionality in hugetlbfs. Preallocate huge * pages to a file in hugetlbfs, and then remove the pages via hole punch. */ @@ -36,7 +34,7 @@ static void run_test(void) free_initial = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE); max_iterations = MIN(free_initial, MAX_PAGES_TO_USE); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); /* First preallocate file with max_iterations pages */ err = fallocate(fd, 0, 0, hpage_size * max_iterations); diff --git a/testcases/kernel/mem/hugetlb/hugefork/hugefork01.c b/testcases/kernel/mem/hugetlb/hugefork/hugefork01.c index 90cefdba..54603b98 100644 --- a/testcases/kernel/mem/hugetlb/hugefork/hugefork01.c +++ b/testcases/kernel/mem/hugetlb/hugefork/hugefork01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This checks copy-on-write semantics, specifically the semantics of a * MAP_PRIVATE mapping across a fork(). Some versions of the powerpc * kernel had a bug in huge_ptep_set_wrprotect() which would fail to @@ -66,7 +64,7 @@ static void run_test(void) static void setup(void) { hpage_size = SAFE_READ_MEMINFO("Hugepagesize:")*1024; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugefork/hugefork02.c b/testcases/kernel/mem/hugetlb/hugefork/hugefork02.c index de495f08..cafeaa24 100644 --- a/testcases/kernel/mem/hugetlb/hugefork/hugefork02.c +++ b/testcases/kernel/mem/hugetlb/hugefork/hugefork02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test shared memory behavior when multiple threads are attached to a * segment. A segment is created and then children are spawned which * attach, write, read (verify), and detach from the shared memory segment. diff --git a/testcases/kernel/mem/hugetlb/hugemmap/Makefile b/testcases/kernel/mem/hugetlb/hugemmap/Makefile index 2d651b4a..6e72e700 100755 --- a/testcases/kernel/mem/hugetlb/hugemmap/Makefile +++ b/testcases/kernel/mem/hugetlb/hugemmap/Makefile @@ -8,4 +8,7 @@ include $(top_srcdir)/include/mk/testcases.mk include $(abs_srcdir)/../Makefile.inc include $(top_srcdir)/include/mk/generic_leaf_target.mk +CFLAGS_no_stack_prot := $(filter-out -fstack-clash-protection, $(CFLAGS)) + hugemmap06: CFLAGS+=-pthread +hugemmap34: CFLAGS=$(CFLAGS_no_stack_prot) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap01.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap01.c index 3fc73000..de513338 100755 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap01.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap01.c @@ -75,7 +75,7 @@ void setup(void) tst_brk(TCONF, "Not enough hugepages for testing."); if (!Hopt) - Hopt = tst_get_tmpdir(); + Hopt = tst_tmpdir_path(); SAFE_MOUNT("none", Hopt, "hugetlbfs", 0, NULL); snprintf(TEMPFILE, sizeof(TEMPFILE), "%s/mmapfile%d", Hopt, getpid()); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap02.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap02.c index e818cd5a..611ae709 100755 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap02.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap02.c @@ -62,7 +62,7 @@ static void test_hugemmap(void) addrlist[i] = addr; } - while (range_is_mapped(low_addr, low_addr + map_sz) == 1) { + while (tst_mapping_in_range(low_addr, low_addr + map_sz) == 1) { low_addr = low_addr + 0x10000000; if (low_addr < LOW_ADDR) @@ -74,7 +74,7 @@ static void test_hugemmap(void) if (addr == MAP_FAILED) tst_brk(TBROK | TERRNO, "mmap failed on nfildes"); - while (range_is_mapped(low_addr2, low_addr2 + map_sz) == 1) { + while (tst_mapping_in_range(low_addr2, low_addr2 + map_sz) == 1) { low_addr2 = low_addr2 + 0x10000000; if (low_addr2 < LOW_ADDR2) @@ -122,7 +122,7 @@ static void setup(void) tst_brk(TCONF, "Not enough hugepages for testing."); if (!Hopt) - Hopt = tst_get_tmpdir(); + Hopt = tst_tmpdir_path(); SAFE_MOUNT("none", Hopt, "hugetlbfs", 0, NULL); snprintf(TEMPFILE, sizeof(TEMPFILE), "%s/mmapfile%d", Hopt, getpid()); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap04.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap04.c index 6af032aa..f52747e6 100755 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap04.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap04.c @@ -93,7 +93,7 @@ void setup(void) tst_brk(TCONF, "Not enough hugepages for testing!"); if (!Hopt) - Hopt = tst_get_tmpdir(); + Hopt = tst_tmpdir_path(); SAFE_MOUNT("none", Hopt, "hugetlbfs", 0, NULL); snprintf(TEMPFILE, sizeof(TEMPFILE), "%s/mmapfile%d", Hopt, getpid()); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap05.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap05.c index d5983fc5..75f28102 100755 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap05.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap05.c @@ -34,7 +34,6 @@ static char path_sys_sz_huge[BUFSIZ]; #define PATH_PROC_VM "/proc/sys/vm/" #define PATH_PROC_OVER PATH_PROC_VM "nr_overcommit_hugepages" #define PATH_PROC_HUGE PATH_PROC_VM "nr_hugepages" -#define PATH_SHMMAX "/proc/sys/kernel/shmmax" /* Only ia64 requires this */ #ifdef __ia64__ diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap06.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap06.c index 79bea8e8..1ac98948 100755 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap06.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap06.c @@ -1,24 +1,26 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015-2017 Red Hat, Inc. + * Copyright (c) Linux Test Project, 2018-2023 * - * DESCRIPTION + * Authors: + * Herton R. Krzesinski + * Li Wang + */ + +/*\ + * There is a race condition if we map a same file on different processes. + * Region tracking is protected by mmap_sem and hugetlb_instantiation_mutex. + * When we do mmap, we don't grab a hugetlb_instantiation_mutex, but only + * mmap_sem (exclusively). This doesn't prevent other tasks from modifying + * the region structure, so it can be modified by two processes concurrently. * - * There is a race condition if we map a same file on different processes. - * Region tracking is protected by mmap_sem and hugetlb_instantiation_mutex. - * When we do mmap, we don't grab a hugetlb_instantiation_mutex, but only - * mmap_sem (exclusively). This doesn't prevent other tasks from modifying - * the region structure, so it can be modified by two processes concurrently. + * This bug was fixed on stable kernel by commits: * - * This bug was fixed on stable kernel by commits: - * f522c3ac00a4(mm, hugetlb: change variable name reservations to resv) - * 9119a41e9091(mm, hugetlb: unify region structure handling) - * 7b24d8616be3(mm, hugetlb: fix race in region tracking) - * 1406ec9ba6c6(mm, hugetlb: improve, cleanup resv_map parameters) - * - * AUTHOR: - * Herton R. Krzesinski - * Li Wang + * f522c3ac00a4 (mm, hugetlb: change variable name reservations to resv) + * 9119a41e9091 (mm, hugetlb: unify region structure handling) + * 7b24d8616be3 (mm, hugetlb: fix race in region tracking) + * 1406ec9ba6c6 (mm, hugetlb: improve, cleanup resv_map parameters) */ #define _GNU_SOURCE @@ -73,8 +75,7 @@ static void do_mmap(unsigned int j LTP_ATTRIBUTE_UNUSED) if (addr == MAP_FAILED) { if (errno == ENOMEM) { - tst_brk(TCONF, - "Cannot allocate hugepage, memory too fragmented?"); + tst_brk(TCONF, "Cannot allocate hugepage, memory too fragmented?"); } tst_brk(TBROK | TERRNO, "Cannot allocate hugepage"); @@ -86,8 +87,7 @@ static void do_mmap(unsigned int j LTP_ATTRIBUTE_UNUSED) TEST(pthread_create(&tid[i], NULL, thr, &mmap_sz[i])); if (TST_RET) - tst_brk(TBROK | TRERRNO, - "pthread_create failed"); + tst_brk(TBROK | TRERRNO, "pthread_create failed"); new_addr = mmap(addr, (sz - 1) * hpage_size, PROT_READ | PROT_WRITE, @@ -103,14 +103,13 @@ static void do_mmap(unsigned int j LTP_ATTRIBUTE_UNUSED) for (i = 0; i < ARSZ; ++i) { TEST(pthread_join(tid[i], NULL)); if (TST_RET) - tst_brk(TBROK | TRERRNO, - "pthread_join failed"); + tst_brk(TBROK | TRERRNO, "pthread_join failed"); } if (munmap(addr, sz * hpage_size) == -1) tst_brk(TFAIL | TERRNO, "huge munmap failed"); - tst_res(TPASS, "No regression found."); + tst_res(TPASS, "No regression found"); } static struct tst_test test = { diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c index 846d22ff..5a965e18 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Certain kernels have a bug where brk() does not perform the same * checks that a MAP_FIXED mmap() will, allowing brk() to create a * normal page VMA in a hugepage only address region. This can lead @@ -112,7 +110,7 @@ cleanup: static void setup(void) { hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024; - huge_fd = tst_creat_unlinked(MNTPOINT, 0); + huge_fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap08.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap08.c index f01a9f36..b9d61dd6 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap08.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap08.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Some kernel versions after hugepage demand allocation was added used a * dubious heuristic to check if there was enough hugepage space available * for a given mapping. The number of not-already-instantiated pages in @@ -117,7 +115,7 @@ static void run_test(unsigned int test_type) static void setup(void) { hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024; - huge_fd = tst_creat_unlinked(MNTPOINT, 0); + huge_fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap09.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap09.c index 336ccdf6..10f64674 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap09.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap09.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test sanity of cow optimization on page cache. If a page in page cache * has only 1 ref count, it is mapped for a private mapping directly and * is overwritten freely, so next time we access the page, we can see @@ -59,7 +57,7 @@ static void run_test(void) static void setup(void) { hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024; - huge_fd = tst_creat_unlinked(MNTPOINT, 0); + huge_fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap10.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap10.c index 0e1b6454..e1795153 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap10.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap10.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This Test perform mmap, munmap and write operation on hugetlb file * based mapping. Mapping can be shared or private. and it checks for * Hugetlb counter (Total, Free, Reserve, Surplus) in /proc/meminfo and @@ -58,7 +56,7 @@ static int kernel_has_private_reservations(void) void *p; read_meminfo_huge(&t, &f, &r, &s); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); p = SAFE_MMAP(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); @@ -182,7 +180,7 @@ static int map_(int s, int hpages, int flags, char *desc, int line) { long et, ef, er, es; - map_fd[s] = tst_creat_unlinked(MNTPOINT, 0); + map_fd[s] = tst_creat_unlinked(MNTPOINT, 0, 0600); map_size[s] = hpages * hpage_size; map_addr[s] = SAFE_MMAP(NULL, map_size[s], PROT_READ|PROT_WRITE, flags, map_fd[s], 0); @@ -449,8 +447,8 @@ static struct tst_test test = { .mntpoint = MNTPOINT, .needs_hugetlbfs = 1, .save_restore = (const struct tst_path_val[]) { - {PATH_OC_HPAGES, NULL}, - {PATH_NR_HPAGES, NULL}, + {PATH_OC_HPAGES, NULL, TST_SR_TCONF}, + {PATH_NR_HPAGES, NULL, TST_SR_TCONF}, {} }, .setup = setup, diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap11.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap11.c index 496a814b..087e8c69 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap11.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap11.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This Test perform Direct Write/Read from/To hugetlbfs file * which is mapped to process address space. The test is checking if it * succeeds and data written or read is not corrupted. @@ -23,7 +21,7 @@ #define P0 "ffffffff" #define IOSZ 4096 -#define NORMAL_PATH "" +#define NORMAL_PATH "./" #define MNTPOINT "hugetlbfs/" static long hpage_size; @@ -34,8 +32,8 @@ static void run_test(void) void *p; char buf[IOSZ] __attribute__((aligned(IOSZ))); - fd = tst_creat_unlinked(MNTPOINT, 0); - nfd = tst_creat_unlinked(NORMAL_PATH, O_DIRECT); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); + nfd = tst_creat_unlinked(NORMAL_PATH, O_DIRECT, 0600); p = SAFE_MMAP(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); memcpy(p, P0, 8); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap12.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap12.c index 81367c51..523ea902 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap12.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap12.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * fadvise() on some kernels can cause the reservation counter to get * corrupted. The problem is that the patches are allocated for the * reservation but not faulted in at the time of allocation. The counters @@ -33,7 +31,7 @@ static void run_test(void) void *p; unsigned long initial_rsvd, map_rsvd, fadvise_rsvd, end_rsvd; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); initial_rsvd = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD); tst_res(TINFO, "Reserve count before map: %lu", initial_rsvd); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap13.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap13.c index f8c36640..206582eb 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap13.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap13.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * On some old ppc64 kernel, when hpage is mmaped on 32 bit boundary and * normal page below it, it triggers the bug caused by off-by-one error. * @@ -21,6 +19,7 @@ #include #include #include +#include #include "hugetlb.h" @@ -44,7 +43,7 @@ static void run_test(void) below_start = FOURGB; above_end = 1024ULL*1024*1024*1024; - if (range_is_mapped(below_start, above_end) == 1) { + if (tst_mapping_in_range(below_start, above_end) == 1) { tst_res(TINFO|TERRNO, "region 4G-IT is not free & " "mmap() failed expected"); tst_res(TPASS, "Successful but inconclusive"); @@ -62,15 +61,15 @@ static void run_test(void) memset(p, 0, hpage_size); /* Test just below 4GB to check for off-by-one errors */ - lowaddr = FOURGB - page_size; - q = mmap((void *)lowaddr, page_size, PROT_READ|PROT_WRITE, + lowaddr = FOURGB - MMAP_GRANULARITY; + q = mmap((void *)lowaddr, MMAP_GRANULARITY, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED|MAP_ANONYMOUS, 0, 0); if (q == MAP_FAILED) { - below_start = FOURGB - page_size; + below_start = FOURGB - MMAP_GRANULARITY; above_end = FOURGB; - if (range_is_mapped(below_start, above_end) == 1) { - tst_res(TINFO|TERRNO, "region (4G-page)-4G is not free & " + if (tst_mapping_in_range(below_start, above_end) == 1) { + tst_res(TINFO|TERRNO, "region (4G-MMAP_GRANULARITY)-4G is not free & " "mmap() failed expected"); tst_res(TPASS, "Successful but inconclusive"); } else @@ -101,7 +100,7 @@ static void setup(void) tst_brk(TCONF, "Machine must be >32 bit"); if (hpage_size > FOURGB) tst_brk(TCONF, "Huge page size is too large"); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap14.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap14.c index c54a746b..5a0fba9b 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap14.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap14.c @@ -5,7 +5,6 @@ */ /*\ - * [Description] * On some old ppc64 kernel, when huge page is mapped at below touching * 32 bit boundary (4GB - hpage_size), and normal page is mmaped * at just above it, it triggers a bug caused by off-by-one error. @@ -50,7 +49,7 @@ static void run_test(void) below_start = FOURGB - 256ULL*1024*1024; above_end = FOURGB; - if (range_is_mapped(below_start, above_end) == 1) { + if (tst_mapping_in_range(below_start, above_end) == 1) { tst_res(TINFO|TERRNO, "region (4G-256M)-4G is not free & " "mmap() failed expected"); tst_res(TPASS, "Successful but inconclusive"); @@ -73,7 +72,7 @@ static void run_test(void) below_start = FOURGB; above_end = FOURGB + page_size; - if (range_is_mapped(below_start, above_end) == 1) { + if (tst_mapping_in_range(below_start, above_end) == 1) { tst_res(TINFO|TERRNO, "region 4G-(4G+page) is not free & " "mmap() failed expected"); tst_res(TPASS, "Successful but inconclusive"); @@ -101,7 +100,7 @@ static void run_test(void) below_start = highaddr; above_end = highaddr + page_size; - if (range_is_mapped(below_start, above_end) == 1) { + if (tst_mapping_in_range(below_start, above_end) == 1) { tst_res(TINFO|TERRNO, "region haddr-(haddr+page) not free & " "mmap() failed unexpected"); tst_res(TPASS, "Successful but inconclusive"); @@ -134,7 +133,7 @@ static void setup(void) tst_brk(TCONF, "Machine must be >32 bit"); if (hpage_size > FOURGB) tst_brk(TCONF, "Huge page size is too large"); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c index 4d198407..1dde9e87 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Older ppc64 kernels don't properly flush dcache to icache before * giving a cleared page to userspace. With some exceedingly * hairy code, this attempts to test for this bug. @@ -29,7 +27,7 @@ #if defined(__powerpc__) || defined(__powerpc64__) || defined(__ia64__) || \ defined(__s390__) || defined(__s390x__) || defined(__sparc__) || \ defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64) || \ - defined(__i386__) || defined(__x86_64__) || defined(__arm__) + defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__loongarch__) #include @@ -47,7 +45,7 @@ static void cacheflush(void *p) { #if defined(__powerpc__) asm volatile("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r"(p)); -#elif defined(__arm__) || defined(__aarch64__) +#elif defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__) __clear_cache(p, p + COPY_SIZE); #else (void)p; @@ -70,7 +68,6 @@ static void jumpfunc(int copy, void *p) memcpy(p, l, COPY_SIZE); cacheflush(p); } - goto *p; dummy: tst_res(TWARN, "unreachable?"); @@ -98,6 +95,14 @@ static void sig_handler(int signum, siginfo_t *si, void *uc) siglongjmp(sig_escape, SUCC_JMP); siglongjmp(sig_escape, FAIL_JMP + SIGILL); } +#elif defined(__loongarch__) + if (signum == SIGILL) { + void *pc = (void *)((ucontext_t *)uc)->uc_mcontext.__pc; + tst_res(TINFO, "SIGILL at %p (sig_expected=%p)", pc, sig_expected); + if (pc == sig_expected) + siglongjmp(sig_escape, SUCC_JMP); + siglongjmp(sig_escape, FAIL_JMP + SIGILL); + } #elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* On x86, zero bytes form a valid instruction: * add %al,(%eax) (i386) @@ -204,7 +209,7 @@ static void run_test(void) SAFE_SIGACTION(SIGBUS, &sa, NULL); SAFE_SIGACTION(SIGSEGV, &sa, NULL); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); for (i = 0; i < NUM_REPETITIONS; i++) if (test_once(fd)) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap16.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap16.c index 2003e701..bd5fae5c 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap16.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap16.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * madvise() on some kernels can cause the reservation counter to get * corrupted. The problem is that the patches are allocated * for the reservation but not faulted in at the time of allocation. The @@ -33,7 +31,7 @@ static void run_test(void) void *p; unsigned long initial_rsvd, map_rsvd, madvise_rsvd, end_rsvd; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); initial_rsvd = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD); tst_res(TINFO, "Reserve count before map: %lu", initial_rsvd); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap17.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap17.c index b8105bbf..b784005a 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap17.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap17.c @@ -5,8 +5,6 @@ */ /*\ - * [Descriptiom] - * * At one stage, a misconversion of hugetlb_vmtruncate_list to a prio_tree * meant that on 32-bit machines, certain combinations of mapping and * truncations could truncate incorrect pages, or overwrite pmds from @@ -79,7 +77,7 @@ static void setup(void) tst_brk(TCONF, "Huge page size is too large"); if (TRUNCATE_POINT % hpage_size) tst_brk(TCONF, "Truncation point is not aligned to huge page size"); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap18.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap18.c index 60707293..45796dec 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap18.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap18.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Just as normal mmap()s can't have an address, length or offset which * is not page aligned, so hugepage mmap()s can't have an address, length * or offset with is not hugepage aligned. @@ -129,7 +127,7 @@ static void setup(void) { hpage_size = SAFE_READ_MEMINFO("Hugepagesize:")*1024; page_size = getpagesize(); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap19.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap19.c index 11060125..13c3c7ac 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap19.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap19.c @@ -6,8 +6,6 @@ */ /*\ - * [Descripiton] - * * At one stage, a misconversion of hugetlb_vmtruncate_list to a * prio_tree meant that on 32-bit machines, truncates at or above 4GB * could truncate lower pages, resulting in BUG_ON()s. @@ -123,7 +121,7 @@ static void setup(void) { page_size = getpagesize(); hpage_size = SAFE_READ_MEMINFO("Hugepagesize:")*1024; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap20.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap20.c index 6bc367f9..313efb4b 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap20.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap20.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * The test checks that mlocking hugetlb areas works with all combinations * of MAP_PRIVATE and MAP_SHARED with and without MAP_LOCKED specified. */ @@ -14,6 +12,7 @@ #include "hugetlb.h" #define MNTPOINT "hugetlbfs/" +#define FLAGS_DESC(x) .flags = x, .flags_str = #x static int fd = -1; static unsigned long hpage_size; @@ -22,10 +21,10 @@ static struct tcase { int flags; char *flags_str; } tcases[] = { - {MAP_PRIVATE, "MAP_PRIVATE"}, - {MAP_SHARED, "MAP_SHARED"}, - {MAP_PRIVATE | MAP_LOCKED, "MAP_PRIVATE | MAP_LOCKED"}, - {MAP_SHARED | MAP_LOCKED, "MAP_SHARED | MAP_LOCKED"}, + { FLAGS_DESC(MAP_PRIVATE) }, + { FLAGS_DESC(MAP_SHARED) }, + { FLAGS_DESC(MAP_PRIVATE | MAP_LOCKED) }, + { FLAGS_DESC(MAP_SHARED | MAP_LOCKED) }, }; static void run_test(unsigned int i) @@ -34,7 +33,7 @@ static void run_test(unsigned int i) void *p; struct tcase *tc = &tcases[i]; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); p = SAFE_MMAP(0, hpage_size, PROT_READ|PROT_WRITE, tc->flags, fd, 0); ret = mlock(p, hpage_size); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap21.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap21.c index a8e332eb..3792cff8 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap21.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap21.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests copy-on-write semantics of large pages where a number of threads * map the same file with the MAP_PRIVATE flag. The threads then write * into their copy of the mapping and recheck the contents to ensure they @@ -36,9 +34,7 @@ static void do_work(int thread, size_t size, int fd) for (i = 0; i < size; i++) memcpy((char *)addr+i, &pattern, 1); - if (msync(addr, size, MS_SYNC)) - tst_brk(TBROK | TERRNO, "Thread %d (pid %d): msync() failed", - thread, getpid()); + SAFE_MSYNC(addr, size, MS_SYNC); for (i = 0; i < size; i++) { if (addr[i] != pattern) { @@ -96,7 +92,7 @@ static void run_test(void) static void setup(void) { hpage_size = tst_get_hugepage_size(); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap22.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap22.c index c2deab47..78e4a3bf 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap22.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap22.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This baseline test validates that a mapping of a certain size can be * created, correctly. Once created, all the pages are filled with a * pattern and rechecked to test for corruption. The mapping is then @@ -29,7 +27,7 @@ static void run_test(unsigned int iter) char pattern = 'A'; size_t size = NR_HUGEPAGES*hpage_size; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); m = SAFE_MMAP(NULL, size, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, 0); for (i = 0; i < NR_HUGEPAGES; i++) { diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap23.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap23.c index 4c1cff32..86157d26 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap23.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap23.c @@ -1,21 +1,22 @@ // SPDX-License-Identifier: LGPL-2.1-or-later /* * Copyright (C) 2005-2006 IBM Corporation. + * Copyright (c) Linux Test Project, 2023 * Author: David Gibson & Adam Litke */ /*\ - * [Description] - * * This test uses mprotect to change protection of hugepage mapping and * perform read/write operation. It checks if the operation results in * expected behaviour as per the protection. */ + #include #include "hugetlb.h" #define MNTPOINT "hugetlbfs/" #define RANDOM_CONSTANT 0x1234ABCD +#define FLAGS_DESC(x) x, #x static int fd = -1; static sigjmp_buf sig_escape; @@ -32,23 +33,12 @@ static struct tcase { int prot2; char *prot2_str; } tcases[] = { - {"R->RW", 1, PROT_READ, "PROT_READ", - 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"}, - - {"RW->R", 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE", - 1, PROT_READ, "PROT_READ"}, - - {"R->RW 1/2", 2, PROT_READ, "PROT_READ", - 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"}, - - {"RW->R 1/2", 2, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE", - 1, PROT_READ, "PROT_READ"}, - - {"NONE->R", 1, PROT_NONE, "PROT_NONE", - 1, PROT_READ, "PROT_READ"}, - - {"NONE->RW", 1, PROT_NONE, "PROT_NONE", - 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"}, + {"R->RW", 1, FLAGS_DESC(PROT_READ), 1, FLAGS_DESC(PROT_READ|PROT_WRITE)}, + {"RW->R", 1, FLAGS_DESC(PROT_READ | PROT_WRITE), 1, FLAGS_DESC(PROT_READ)}, + {"R->RW 1/2", 2, FLAGS_DESC(PROT_READ), 1, FLAGS_DESC(PROT_READ | PROT_WRITE)}, + {"RW->R 1/2", 2, FLAGS_DESC(PROT_READ | PROT_WRITE), 1, FLAGS_DESC(PROT_READ)}, + {"NONE->R", 1, FLAGS_DESC(PROT_NONE), 1, FLAGS_DESC(PROT_READ)}, + {"NONE->RW", 1, FLAGS_DESC(PROT_NONE), 1, FLAGS_DESC(PROT_READ | PROT_WRITE)}, }; static void sig_handler(int signum, siginfo_t *si, void *uc) @@ -205,7 +195,7 @@ static void setup(void) hpage_size = tst_get_hugepage_size(); SAFE_SIGACTION(SIGSEGV, &sa, NULL); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); addr = SAFE_MMAP(NULL, 2*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); memset(addr, 0, hpage_size); SAFE_MUNMAP(addr, hpage_size); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap24.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap24.c index 158a0301..0de44d58 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap24.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap24.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Kernel has bug in mremap for some architecture. mremap() can cause * crashes on architectures with holes in the address space (like ia64) * and on powerpc with it's distict page size slices. @@ -30,7 +28,7 @@ static int init_slice_boundary(int fd) unsigned long slice_size; void *p, *heap; int i; -#if defined(__LP64__) && !defined(__aarch64__) +#if defined(__LP64__) && !defined(__aarch64__) && !defined(__loongarch__) /* powerpc: 1TB slices starting at 1 TB */ slice_boundary = 0x10000000000; slice_size = 0x10000000000; @@ -42,7 +40,6 @@ static int init_slice_boundary(int fd) /* dummy malloc so we know where is heap */ heap = malloc(1); - free(heap); /* Avoid underflow on systems with large huge pages. * The additionally plus heap address is to reduce the possibility @@ -51,6 +48,8 @@ static int init_slice_boundary(int fd) while (slice_boundary + slice_size < (unsigned long)heap + 2*hpage_size) slice_boundary += slice_size; + free(heap); + /* Find 2 neighbour slices with couple huge pages free * around slice boundary. * 16 is the maximum number of slices (low/high) @@ -84,7 +83,7 @@ static void run_test(void) long p_size, q_size; int ret; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); ret = init_slice_boundary(fd); if (ret) goto cleanup; diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap25.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap25.c index 71beb90d..7d9fd0b6 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap25.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap25.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * The kernel has bug for mremap() on some architecture. mremap() can * cause crashes on architectures with holes in the address space * (like ia64) and on powerpc with it's distinct page size "slices". @@ -76,7 +74,7 @@ static void run_test(void) void *p; int ret; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); p = map_align(3*hpage_size, hpage_size); SAFE_MUNMAP(p, hpage_size); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap26.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap26.c index 609f2b63..cd60d740 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap26.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap26.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test Description: The kernel has bug for mremap() on some architecture. * mremap() can cause crashes on architectures with holes in the address * space (like ia64) and on powerpc with it's distinct page size "slices". @@ -55,7 +53,7 @@ static void run_test(void) void *p; int ret; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); p = SAFE_MMAP(NULL, 3*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); SAFE_MUNMAP(p, hpage_size); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap27.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap27.c index 218d9e19..f79258da 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap27.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap27.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test to preserve a reserved page against no-reserved mapping. If all * hugepages are reserved, access to no-reserved shared mapping cause a * process die, instead of stealing a hugepage which is reserved for other @@ -99,8 +97,8 @@ cleanup: static void setup(void) { hpage_size = tst_get_hugepage_size(); - fd1 = tst_creat_unlinked(MNTPOINT, 0); - fd2 = tst_creat_unlinked(MNTPOINT, 0); + fd1 = tst_creat_unlinked(MNTPOINT, 0, 0600); + fd2 = tst_creat_unlinked(MNTPOINT, 0, 0600); } static void cleanup(void) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap28.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap28.c index 060d1c85..48cedd15 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap28.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap28.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test to correct handling for reserve count. If no reserved mapping is * created to reserved file region, it should be considered as reserve * mapping. Otherwise, reserve count will be overflowed. @@ -26,7 +24,7 @@ static void run_test(void) initial_resv = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD); - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); p = SAFE_MMAP(NULL, hpage_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); q = SAFE_MMAP(NULL, hpage_size, diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap29.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap29.c index 6bff2c8e..f0497450 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap29.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap29.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * The test do mmap() with shared mapping and write. It matches the data * with private mmap() and then change it with other data. It checks * shared mapping data if data is not contaiminated by private mapping. @@ -29,7 +27,7 @@ static void run_test(void) unsigned int *pl, *ql; unsigned long i; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); p = SAFE_MMAP(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap30.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap30.c index 7ed9046f..a624f4a2 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap30.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap30.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * readahead() on some kernels can cause the reservation counter to get * corrupted. The problem is that the pages are allocated for the * reservation but not faulted in at the time of allocation. The @@ -27,7 +25,7 @@ static void run_test(void) void *p; unsigned long initial_rsvd, map_rsvd, readahead_rsvd, end_rsvd; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); initial_rsvd = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD); p = SAFE_MMAP(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap31.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap31.c index 9072e9de..57367b64 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap31.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap31.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This test is basic shared mapping test. Two shared mappings are created * with same offset on a file. It checks if writing to one mapping can be * seen to other mapping or not? @@ -26,7 +24,7 @@ static void run_test(void) unsigned long *pl, *ql; unsigned long i; - fd = tst_creat_unlinked(MNTPOINT, 0); + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); p = SAFE_MMAP(NULL, hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap32.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap32.c index d27e5b8b..188ee217 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap32.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap32.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Before kernel version 5.10-rc7, there was a bug that resulted in a "Bad Page * State" error when freeing gigantic hugepages. This happened because the * struct page entry compound_nr, which overlapped with page->mapping in the diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap34.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap34.c new file mode 100644 index 00000000..bf2b3564 --- /dev/null +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap34.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2005-2006 IBM Corporation + * Author: David Gibson & Adam Litke + */ + +/*\ + * On PowerPC, the address space is divided into segments. These segments can + * contain either huge pages or normal pages, but not both. All segments are + * initially set up to map normal pages. When a huge page mapping is created + * within a set of empty segments, they are "enabled" for huge pages at that + * time. Once enabled for huge pages, they can not be used again for normal + * pages for the remaining lifetime of the process. + * + * If the segment immediately preceeding the segment containing the stack is + * converted to huge pages and the stack is made to grow into the this + * preceeding segment, some kernels may attempt to map normal pages into the + * huge page-only segment -- resulting in bugs. + */ + +#define _GNU_SOURCE +#include "lapi/mmap.h" +#include "hugetlb.h" +#include +#include +#include + +#ifdef __LP64__ +#define STACK_ALLOCATION_SIZE (256*1024*1024) +#else +#define STACK_ALLOCATION_SIZE (16*1024*1024) +#endif +#define MNTPOINT "hugetlbfs/" +#define PATH_HUGEPAGE "/sys/kernel/mm/hugepages" + +#define STACKS_MAX 64 + +static int fd = -1; +static uintptr_t hpage_size, stacks[STACKS_MAX]; +static int stacks_num; +static void *hpage_addr, *stack_addr; +static void **shared_area; + +int do_child(void *stop_address) +{ + volatile char *x; + + /* corefile from this process is not interesting and limiting + * its size can save a lot of time. '1' is a special value, + * that will also abort dumping via pipe, which by default + * sets limit to RLIM_INFINITY. + */ + tst_no_corefile(1); + tst_res(TINFO, "Child process starting with top of stack at %p", &x); + + do { + x = alloca(STACK_ALLOCATION_SIZE); + *shared_area = (void *)x; + *x = 1; + } while ((void *)x >= stop_address); + exit(0); +} + +static void run_test(void) +{ + int pid, status; + + pid = ltp_clone(CLONE_VM | CLONE_VFORK | SIGCHLD, do_child, + hpage_addr, hpage_size, stack_addr); + if (pid == 0) + do_child(hpage_addr); + + SAFE_WAITPID(pid, &status, 0); + tst_res(TINFO, "Child process extended stack up to %p, hasn't reached %p", + *shared_area, *shared_area - STACK_ALLOCATION_SIZE); + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) + tst_res(TPASS, "Child killed by %s as expected", tst_strsig(SIGSEGV)); + else + tst_res(TFAIL, "Child: %s", tst_strstatus(status)); +} + +/* Return start address of next mapping or 0 */ +static uintptr_t get_next_mapping_start(uintptr_t addr) +{ + FILE *fp = fopen("/proc/self/maps", "r"); + + if (fp == NULL) + tst_brk(TBROK | TERRNO, "Failed to open /proc/self/maps."); + + while (!feof(fp)) { + uintptr_t start, end; + int ret; + + ret = fscanf(fp, "%"PRIxPTR"-%"PRIxPTR" %*[^\n]\n", &start, &end); + if (ret != 2) { + fclose(fp); + tst_brk(TBROK | TERRNO, "Couldn't parse /proc/self/maps line."); + } + + if (start > addr) { + fclose(fp); + return start; + } + } + fclose(fp); + return 0; +} + +static int is_addr_in_stacks(uintptr_t addr) +{ + int i; + + for (i = 0; i < stacks_num; i++) { + if (addr == stacks[i]) + return 1; + } + return 0; +} + +void setup(void) +{ + struct rlimit r; + int i; + + hpage_size = tst_get_hugepage_size(); + /* + * Setting the stack size to unlimited. + */ + r.rlim_cur = RLIM_INFINITY; + r.rlim_max = RLIM_INFINITY; + SAFE_SETRLIMIT(RLIMIT_STACK, &r); + SAFE_GETRLIMIT(RLIMIT_STACK, &r); + if (r.rlim_cur != RLIM_INFINITY) + tst_brk(TCONF, "Stack rlimit must be 'unlimited'"); + + fd = tst_creat_unlinked(MNTPOINT, 0, 0600); + + /* shared memory to pass a (void *) from child process */ + shared_area = SAFE_MMAP(0, getpagesize(), PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_ANONYMOUS, -1, 0); + + /* + * We search for potential stack addresses by looking at + * places where kernel would map next huge page and occupying that + * address as potential stack. When huge page lands in such place + * that next mapping is one of our stack mappings, we use those + * two for the test. We try to map huge pages in child process so that + * slices in parent are not affected. + */ + tst_res(TINFO, "searching for huge page and child stack placement"); + for (i = 0; i < STACKS_MAX; i++) { + uintptr_t next_start; + int pid, status; + + pid = SAFE_FORK(); + if (pid == 0) { + *shared_area = SAFE_MMAP(0, hpage_size, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + SAFE_MUNMAP(*shared_area, hpage_size); + exit(0); + } + SAFE_WAITPID(pid, &status, 0); + if (status != 0) + tst_brk(TFAIL, "Child: %s", tst_strstatus(status)); + + next_start = get_next_mapping_start((uintptr_t)*shared_area); + if (is_addr_in_stacks(next_start)) { + stack_addr = (void *)next_start; + hpage_addr = *shared_area; + break; + } + + tst_res(TINFO, "potential stack at address %p", *shared_area); + stacks[stacks_num++] = (uintptr_t) SAFE_MMAP(*shared_area, hpage_size, + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE|MAP_GROWSDOWN, -1, 0); + } + + if (!stack_addr) + tst_brk(TCONF, "failed to find good place for huge page and stack"); + + hpage_addr = mmap(hpage_addr, hpage_size, PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_FIXED, fd, 0); + if (hpage_addr == MAP_FAILED) { + if (errno == ENOMEM) + tst_brk(TCONF, "Not enough memory."); + tst_brk(TBROK|TERRNO, "mmap failed"); + } + tst_res(TINFO, "stack = %p-%p, hugepage = %p-%p", stack_addr, stack_addr+hpage_size, + hpage_addr, hpage_addr+hpage_size); +} + +void cleanup(void) +{ + if (fd > 0) + SAFE_CLOSE(fd); +} + +static struct tst_test test = { + .tags = (struct tst_tag[]) { + {"linux-git", "0d59a01bc461"}, + {} + }, + .needs_root = 1, + .mntpoint = MNTPOINT, + .needs_hugetlbfs = 1, + .needs_tmpdir = 1, + .setup = setup, + .cleanup = cleanup, + .test_all = run_test, + .hugepages = {1, TST_NEEDS}, + .forks_child = 1, + .supported_archs = (const char *const []){"ppc", "ppc64", NULL}, +}; diff --git a/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat04.c b/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat04.c index 8ad745d5..9f877fd0 100755 --- a/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat04.c +++ b/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat04.c @@ -55,7 +55,7 @@ static void shared_hugepage(void) tst_brk(TBROK | TERRNO, "shmget"); while (boundary <= BOUNDARY_MAX - && range_is_mapped(boundary, boundary+SIZE)) + && tst_mapping_in_range(boundary, boundary+SIZE)) boundary += 128*1024*1024; if (boundary > BOUNDARY_MAX) tst_brk(TCONF, "failed to find free unmapped range"); @@ -82,7 +82,7 @@ static void setup(void) long hpage_size, orig_hugepages; unsigned long new_shmmax; - orig_hugepages = get_sys_tune("nr_hugepages"); + orig_hugepages = TST_SYS_CONF_LONG_GET("/proc/sys/vm/nr_hugepages"); SAFE_FILE_SCANF(PATH_SHMMAX, "%lu", &new_shmmax); if (new_shmmax < SIZE) diff --git a/testcases/kernel/mem/hugetlb/hugeshmctl/hugeshmctl01.c b/testcases/kernel/mem/hugetlb/hugeshmctl/hugeshmctl01.c index a68b0da7..bd4437b1 100755 --- a/testcases/kernel/mem/hugetlb/hugeshmctl/hugeshmctl01.c +++ b/testcases/kernel/mem/hugetlb/hugeshmctl/hugeshmctl01.c @@ -1,35 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2004 - * Copyright (c) Linux Test Project, 2004-2020 + * Copyright (c) Linux Test Project, 2004-2023 + * Original author: Wayne Boyer, modified by Robbie Williamson */ -/* - * DESCRIPTION - * hugeshmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as - * they are used with shmctl() - * - * ALGORITHM - * loop if that option was specified - * create a large shared memory segment with read and write permission - * set up any test case specific conditions - * call shmctl() using the TEST macro - * check the return code - * if failure, issue a FAIL message. - * otherwise, - * if doing functionality testing - * call the correct test function - * if the conditions are correct, - * issue a PASS message - * otherwise - * issue a FAIL message - * otherwise - * issue a PASS message - * call cleanup - * - * HISTORY - * 03/2001 - Written by Wayne Boyer - * 04/2004 - Updated by Robbie Williamson +/*\ + * Test the IPC_STAT, IPC_SET and IPC_RMID commands used by shmctl(). */ #include @@ -71,9 +48,11 @@ static void test_hugeshmctl(unsigned int i) * permissions. Do this here instead of in setup() * so that looping (-i) will work correctly. */ - if (i == 0) + if (i == 0) { shm_id_1 = shmget(shmkey, shm_size, SHM_HUGETLB | IPC_CREAT | IPC_EXCL | SHM_RW); + } + if (shm_id_1 == -1) tst_brk(TBROK | TERRNO, "shmget #main"); diff --git a/testcases/kernel/mem/hugetlb/hugeshmget/hugeshmget06.c b/testcases/kernel/mem/hugetlb/hugeshmget/hugeshmget06.c new file mode 100644 index 00000000..ca93ae6d --- /dev/null +++ b/testcases/kernel/mem/hugetlb/hugeshmget/hugeshmget06.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2005-2006, IBM Corporation. + * Author: David Gibson & Adam Litke + */ + +/*\ + * This testcase creates shared memory segments backed by hugepages, + * writes specific patterns to each segment, verifies pattern, + * and detaches a shared memory segments in a loop. + * It ensures that the hugepage backed shared memory functionalities + * works correctly by validating the data written to segment. + */ + +#include "hugetlb.h" +#include "tst_safe_sysv_ipc.h" + +#define NR_HUGEPAGES 4 + +static long hpage_size; +static int shmid = -1; + +static void run_test(void) +{ + size_t i, j; + char pattern; + char *shmaddr; + + shmaddr = SAFE_SHMAT(shmid, 0, SHM_RND); + tst_res(TINFO, "shmaddr: %p", shmaddr); + + for (i = 0; i < NR_HUGEPAGES; i++) { + pattern = 65 + (i % 26); + tst_res(TDEBUG, "Touching %p with %c", + shmaddr + (i * hpage_size), pattern); + memset(shmaddr + (i * hpage_size), pattern, hpage_size); + } + + for (i = 0; i < NR_HUGEPAGES; i++) { + pattern = 65 + (i % 26); + tst_res(TDEBUG, "Verifying %p", (shmaddr + (i * hpage_size))); + for (j = 0; j < (size_t)hpage_size; j++) + if (*(shmaddr + (i * hpage_size) + j) != pattern) { + tst_res(TFAIL, "Got wrong byte 0x%02x expected 0x%02x", + *(shmaddr + (i * hpage_size) + j), + pattern); + return; + } + } + SAFE_SHMDT((const void *)shmaddr); + tst_res(TPASS, "shm hugepages works correctly"); +} + +static void setup(void) +{ + hpage_size = tst_get_hugepage_size(); + tst_res(TINFO, "hugepage size is %ld", hpage_size); + shmid = SAFE_SHMGET(IPC_PRIVATE, NR_HUGEPAGES * hpage_size, SHM_HUGETLB|IPC_CREAT|SHM_R|SHM_W); + tst_res(TINFO, "shmid: 0x%x", shmid); +} + +static void cleanup(void) +{ + if (shmid >= 0) + SAFE_SHMCTL(shmid, IPC_RMID, NULL); +} + +static struct tst_test test = { + .needs_root = 1, + .setup = setup, + .cleanup = cleanup, + .test_all = run_test, + .hugepages = {NR_HUGEPAGES, TST_NEEDS}, +}; diff --git a/testcases/kernel/mem/hugetlb/lib/Makefile b/testcases/kernel/mem/hugetlb/lib/Makefile index ceccd261..45606b17 100755 --- a/testcases/kernel/mem/hugetlb/lib/Makefile +++ b/testcases/kernel/mem/hugetlb/lib/Makefile @@ -4,7 +4,6 @@ top_srcdir ?= ../../../../.. include $(top_srcdir)/include/mk/env_pre.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk INTERNAL_LIB := libhugetlb.a diff --git a/testcases/kernel/mem/hugetlb/lib/hugetlb.c b/testcases/kernel/mem/hugetlb/lib/hugetlb.c index 43a677ce..6a2976a5 100755 --- a/testcases/kernel/mem/hugetlb/lib/hugetlb.c +++ b/testcases/kernel/mem/hugetlb/lib/hugetlb.c @@ -130,3 +130,14 @@ int do_readback(void *p, size_t size, char *desc) } return 0; } + +void update_shm_size(size_t * shm_size) +{ + size_t shmmax; + + SAFE_FILE_SCANF(PATH_SHMMAX, "%zu", &shmmax); + if (*shm_size > shmmax) { + tst_res(TINFO, "Set shm_size to shmmax: %zu", shmmax); + *shm_size = shmmax; + } +} diff --git a/testcases/kernel/mem/hugetlb/lib/hugetlb.h b/testcases/kernel/mem/hugetlb/lib/hugetlb.h index 34fe08c2..22975c99 100755 --- a/testcases/kernel/mem/hugetlb/lib/hugetlb.h +++ b/testcases/kernel/mem/hugetlb/lib/hugetlb.h @@ -17,8 +17,6 @@ #include #include #include "tst_test.h" -#include "old_tmpdir.h" -#include "mem.h" #define PALIGN(p, a) ((void *)LTP_ALIGN((unsigned long)(p), (a))) @@ -30,6 +28,8 @@ #define SHM_HUGETLB 04000 /* segment is mapped via hugetlb */ #endif +#define PATH_SHMMAX "/proc/sys/kernel/shmmax" + #ifndef barrier # ifdef mb /* Redefining the mb() */ @@ -57,4 +57,7 @@ int getipckey(void); int getuserid(char *user); void rm_shm(int shm_id); int do_readback(void *p, size_t size, char *desc); + +void update_shm_size(size_t *shm_size); + #endif /* hugetlb.h */ diff --git a/testcases/kernel/mem/include/libmem.mk b/testcases/kernel/mem/include/libmem.mk deleted file mode 100755 index b6d45f0f..00000000 --- a/testcases/kernel/mem/include/libmem.mk +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright (C) 2012 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See -# the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# - -MEM_SRCDIR := $(abs_top_srcdir)/testcases/kernel/mem -LIBMEM_SRCDIR := $(MEM_SRCDIR)/lib - -MEM_DIR := $(top_builddir)/testcases/kernel/mem -LIBMEM_DIR := $(MEM_DIR)/lib -LIBMEM := $(LIBMEM_DIR)/libmem.a -FILTER_OUT_DIRS := $(LIBMEM_DIR) -CFLAGS += -I$(MEM_SRCDIR)/include -pthread -LDLIBS += $(NUMA_LIBS) -lmem -lltp -LDFLAGS += -L$(LIBMEM_DIR) - -$(LIBMEM_DIR): - mkdir -p "$@" - -$(LIBMEM): $(LIBMEM_DIR) - $(MAKE) -C $^ -f "$(LIBMEM_SRCDIR)/Makefile" all - -MAKE_DEPS += $(LIBMEM) - -trunk-clean:: | lib-clean - -lib-clean:: $(LIBMEM_DIR) - $(MAKE) -C $^ -f "$(LIBMEM_SRCDIR)/Makefile" clean - -include $(top_srcdir)/testcases/kernel/include/lib.mk diff --git a/testcases/kernel/mem/include/mem.h b/testcases/kernel/mem/include/mem.h deleted file mode 100755 index cdc3ca14..00000000 --- a/testcases/kernel/mem/include/mem.h +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) Linux Test Project, 2011-2021 - */ -#ifndef _MEM_H -#define _MEM_H -#include "config.h" -#include "tst_test.h" -#include "ksm_helper.h" -#include "tst_memutils.h" - -#if defined(__powerpc__) || defined(__powerpc64__) -#define MAXNODES 256 -#else -#define MAXNODES 512 -#endif -#define MB (1UL<<20) -#define KB (1UL<<10) -#define PATH_SYS_SYSTEM "/sys/devices/system" -#define PATH_SYSVM "/proc/sys/vm/" -#define PATH_MEMINFO "/proc/meminfo" -#define BITS_PER_LONG (8 * sizeof(long)) - -static inline void set_node(unsigned long *array, unsigned int node) -{ - array[node / BITS_PER_LONG] |= 1UL << (node % BITS_PER_LONG); -} - -static inline void clean_node(unsigned long *array) -{ - unsigned int i; - - for (i = 0; i < MAXNODES / BITS_PER_LONG; i++) - array[i] &= 0UL; -} - -/* OOM */ - -#define LENGTH (3UL<<30) -#define TESTMEM (1UL<<30) -#define NORMAL 1 -#define MLOCK 2 -#define KSM 3 - -void oom(int testcase, int lite, int retcode, int allow_sigkill); -void testoom(int mempolicy, int lite, int retcode, int allow_sigkill); - -/* KSM */ - -void create_same_memory(int size, int num, int unit); -void test_ksm_merge_across_nodes(unsigned long nr_pages); -void ksm_group_check(int run, int pg_shared, int pg_sharing, int pg_volatile, - int pg_unshared, int sleep_msecs, int pages_to_scan); - -/* THP */ - -#define PATH_THP "/sys/kernel/mm/transparent_hugepage/" -#define PATH_KHPD PATH_THP "khugepaged/" - -/* HUGETLB */ - -#define PATH_HUGEPAGES "/sys/kernel/mm/hugepages/" -#define PATH_SHMMAX "/proc/sys/kernel/shmmax" - -void check_hugepage(void); -void write_memcg(void); - -/* cpuset/memcg - include/tst_cgroup.h */ -void write_cpusets(const struct tst_cg_group *cg, long nd); - -/* shared */ -unsigned int get_a_numa_node(void); -int path_exist(const char *path, ...); -void set_sys_tune(char *sys_file, long tune, int check); -long get_sys_tune(char *sys_file); - -void update_shm_size(size_t *shm_size); - -/* MMAP */ -int range_is_mapped(unsigned long low, unsigned long high); -#endif diff --git a/testcases/kernel/mem/ksm/Makefile b/testcases/kernel/mem/ksm/Makefile index 23662569..2af02a27 100755 --- a/testcases/kernel/mem/ksm/Makefile +++ b/testcases/kernel/mem/ksm/Makefile @@ -3,9 +3,9 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpnuma +LTPLIBS = numa ksm06: LTPLDLIBS = -lltpnuma include $(top_srcdir)/include/mk/testcases.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk +include $(top_srcdir)/testcases/kernel/include/lib.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/mem/ksm/ksm01.c b/testcases/kernel/mem/ksm/ksm01.c index bcd09586..a22e4830 100755 --- a/testcases/kernel/mem/ksm/ksm01.c +++ b/testcases/kernel/mem/ksm/ksm01.c @@ -56,7 +56,7 @@ #include #include #include -#include "mem.h" +#include "tst_test.h" #include "ksm_common.h" static void verify_ksm(void) @@ -86,6 +86,8 @@ static struct tst_test test = { TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, {"/sys/kernel/mm/ksm/merge_across_nodes", "1", TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, + {"/sys/kernel/mm/ksm/smart_scan", "0", + TST_SR_SKIP_MISSING | TST_SR_TBROK_RO}, {} }, .needs_kconfigs = (const char *const[]){ diff --git a/testcases/kernel/mem/ksm/ksm02.c b/testcases/kernel/mem/ksm/ksm02.c index bce639dc..ab16af29 100755 --- a/testcases/kernel/mem/ksm/ksm02.c +++ b/testcases/kernel/mem/ksm/ksm02.c @@ -53,7 +53,7 @@ #include #include #include -#include "mem.h" +#include "tst_test.h" #include "ksm_common.h" #ifdef HAVE_NUMA_V2 @@ -76,7 +76,7 @@ static void verify_ksm(void) } create_same_memory(size, num, unit); - write_cpusets(tst_cg, node); + write_node_cpusets(tst_cg, node); SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid()); create_same_memory(size, num, unit); SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid()); @@ -87,7 +87,7 @@ static void setup(void) parse_ksm_options(opt_sizestr, &size, opt_numstr, &num, opt_unitstr, &unit); if (opt_sizestr && size > DEFAULT_MEMSIZE) - tst_set_max_runtime(32 * (size / DEFAULT_MEMSIZE)); + tst_set_timeout(32 * (size / DEFAULT_MEMSIZE)); } static struct tst_test test = { @@ -107,6 +107,8 @@ static struct tst_test test = { TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, {"/sys/kernel/mm/ksm/merge_across_nodes", "1", TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, + {"/sys/kernel/mm/ksm/smart_scan", "0", + TST_SR_SKIP_MISSING | TST_SR_TBROK_RO}, {} }, .needs_kconfigs = (const char *const[]){ @@ -114,7 +116,7 @@ static struct tst_test test = { NULL }, .test_all = verify_ksm, - .max_runtime = 32, + .timeout = 32, .needs_cgroup_ctrls = (const char *const []){ "cpuset", NULL }, }; diff --git a/testcases/kernel/mem/ksm/ksm03.c b/testcases/kernel/mem/ksm/ksm03.c index 4a733269..78844b30 100755 --- a/testcases/kernel/mem/ksm/ksm03.c +++ b/testcases/kernel/mem/ksm/ksm03.c @@ -56,7 +56,7 @@ #include #include #include -#include "mem.h" +#include "tst_test.h" #include "ksm_common.h" static void verify_ksm(void) @@ -89,6 +89,8 @@ static struct tst_test test = { TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, {"/sys/kernel/mm/ksm/merge_across_nodes", "1", TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, + {"/sys/kernel/mm/ksm/smart_scan", "0", + TST_SR_SKIP_MISSING | TST_SR_TBROK_RO}, {} }, .needs_kconfigs = (const char *const[]){ diff --git a/testcases/kernel/mem/ksm/ksm04.c b/testcases/kernel/mem/ksm/ksm04.c index 4f1f2f72..26506b4f 100755 --- a/testcases/kernel/mem/ksm/ksm04.c +++ b/testcases/kernel/mem/ksm/ksm04.c @@ -1,34 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2010-2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * Kernel Samepage Merging (KSM) for Memory Resource Controller and NUMA - * - * Basic tests were to start several programs with same and different - * memory contents and ensure only to merge the ones with the same - * contents. When changed the content of one of merged pages in a - * process and to the mode "unmerging", it should discard all merged - * pages there. Also tested it is possible to disable KSM. There are - * also command-line options to specify the memory allocation size, and - * number of processes have same memory contents so it is possible to - * test more advanced things like KSM + OOM etc. - * + */ + +/*\ * Prerequisites: * - * 1) ksm and ksmtuned daemons need to be disabled. Otherwise, it could - * distrub the testing as they also change some ksm tunables depends - * on current workloads. + * ksm and ksmtuned daemons need to be disabled. Otherwise, it could + * distrub the testing as they also change some ksm tunables depends + * on current workloads. + * + * [Algorithm] * - * The test steps are: * - Check ksm feature and backup current run setting. * - Change run setting to 1 - merging. * - 3 memory allocation programs have the memory contents that 2 of @@ -53,7 +36,7 @@ #include #include #include -#include "mem.h" +#include "tst_test.h" #include "ksm_common.h" #ifdef HAVE_NUMA_V2 @@ -78,7 +61,7 @@ static void verify_ksm(void) } create_same_memory(size, num, unit); - write_cpusets(tst_cg, node); + write_node_cpusets(tst_cg, node); create_same_memory(size, num, unit); } @@ -89,7 +72,7 @@ static void setup(void) SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid()); if (opt_sizestr && size > DEFAULT_MEMSIZE) - tst_set_max_runtime(32 * (size / DEFAULT_MEMSIZE)); + tst_set_timeout(32 * (size / DEFAULT_MEMSIZE)); } static struct tst_test test = { @@ -109,6 +92,8 @@ static struct tst_test test = { TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, {"/sys/kernel/mm/ksm/merge_across_nodes", "1", TST_SR_SKIP_MISSING | TST_SR_TCONF_RO}, + {"/sys/kernel/mm/ksm/smart_scan", "0", + TST_SR_SKIP_MISSING | TST_SR_TBROK_RO}, {} }, .needs_kconfigs = (const char *const[]){ @@ -116,7 +101,7 @@ static struct tst_test test = { NULL }, .test_all = verify_ksm, - .max_runtime = 32, + .timeout = 32, .needs_cgroup_ctrls = (const char *const []){ "memory", "cpuset", NULL }, diff --git a/testcases/kernel/mem/ksm/ksm05.c b/testcases/kernel/mem/ksm/ksm05.c index 1f58c832..025dffc0 100755 --- a/testcases/kernel/mem/ksm/ksm05.c +++ b/testcases/kernel/mem/ksm/ksm05.c @@ -39,11 +39,10 @@ #include #include #include "tst_test.h" -#include "mem.h" +#include "ksm_helper.h" #ifdef HAVE_DECL_MADV_MERGEABLE -static int ksm_run_orig = -1; static void sighandler(int sig); static void test_ksm(void) @@ -89,6 +88,8 @@ static struct tst_test test = { .test_all = test_ksm, .save_restore = (const struct tst_path_val[]) { {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK}, + {"/sys/kernel/mm/ksm/smart_scan", "0", + TST_SR_SKIP_MISSING | TST_SR_TBROK_RO}, {} }, .needs_kconfigs = (const char *const[]){ diff --git a/testcases/kernel/mem/ksm/ksm06.c b/testcases/kernel/mem/ksm/ksm06.c index 0b159e5c..a8e73275 100755 --- a/testcases/kernel/mem/ksm/ksm06.c +++ b/testcases/kernel/mem/ksm/ksm06.c @@ -3,8 +3,6 @@ * Copyright (C) 2013-2017 Red Hat, Inc. */ /*\ - * [Description] - * * The case is designed to test sysfs boolean knob * /sys/kernel/mm/ksm/merge_across_nodes. * @@ -33,8 +31,10 @@ #include #include -#include "mem.h" +#include "tst_test.h" #include "tst_numa.h" +#include "ksm_helper.h" +#include "ksm_test.h" #ifdef HAVE_NUMA_V2 # include @@ -142,6 +142,8 @@ static struct tst_test test = { {"/sys/kernel/mm/ksm/run", NULL, TST_SR_TBROK}, {"/sys/kernel/mm/ksm/sleep_millisecs", NULL, TST_SR_TBROK}, {"/sys/kernel/mm/ksm/merge_across_nodes", NULL, TST_SR_TCONF}, + {"/sys/kernel/mm/ksm/smart_scan", "0", + TST_SR_SKIP_MISSING | TST_SR_TBROK_RO}, {} }, .needs_kconfigs = (const char *const[]){ diff --git a/testcases/kernel/mem/ksm/ksm07.c b/testcases/kernel/mem/ksm/ksm07.c new file mode 100644 index 00000000..6b97b459 --- /dev/null +++ b/testcases/kernel/mem/ksm/ksm07.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Red Hat, Inc. + */ +/*\ + * Kernel Samepage Merging (KSM) for smart scan feature + * + * Test allocates a page and fills it with 'a' characters. It captures the + * pages_skipped counter, waits for a few iterations and captures the + * pages_skipped counter again. The expectation is that over 50% of the page + * scans are skipped. (There is only one page that has KSM enabled and it gets + * scanned during each iteration and it cannot be de-duplicated.) + * + * Smart scan feature was added in kernel v6.7. + * + * [Prerequisites] + * + * ksm and ksmtuned daemons need to be disabled. Otherwise, it could + * distrub the testing as they also change some ksm tunables depends + * on current workloads. + */ + +#include +#include "tst_test.h" +#include "ksm_helper.h" + +/* This test allocates one page, fills the page with a's, captures the + * full_scan and pages_skipped counters. Then it makes sure at least 3 + * full scans have been performed and measures the above counters again. + * The expectation is that at least 50% of the pages are skipped. + * + * To wait for at least 3 scans it uses the wait_ksmd_full_scan() function. In + * reality, it will be a lot more scans as the wait_ksmd_full_scan() function + * sleeps for one second. + */ +static void verify_ksm(void) +{ + int full_scans_begin; + int full_scans_end; + int pages_skipped_begin; + int pages_skipped_end; + int diff_pages; + int diff_scans; + unsigned long page_size; + char *memory; + + /* Apply for the space for memory. */ + page_size = sysconf(_SC_PAGE_SIZE); + memory = SAFE_MALLOC(page_size); + memory = SAFE_MMAP(NULL, page_size, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); +#ifdef HAVE_DECL_MADV_MERGEABLE + if (madvise(memory, page_size, MADV_MERGEABLE) == -1) + tst_brk(TBROK|TERRNO, "madvise"); +#endif + memset(memory, 'a', page_size); + + tst_res(TINFO, "KSM merging"); + + if (access(PATH_KSM "max_page_sharing", F_OK) == 0) + SAFE_FILE_PRINTF(PATH_KSM "run", "2"); + + /* Set defalut ksm scan values. */ + SAFE_FILE_PRINTF(PATH_KSM "run", "1"); + SAFE_FILE_PRINTF(PATH_KSM "pages_to_scan", "%ld", 100l); + SAFE_FILE_PRINTF(PATH_KSM "sleep_millisecs", "0"); + + /* Measure pages skipped aka "smart scan". */ + SAFE_FILE_SCANF(PATH_KSM "full_scans", "%d", &full_scans_begin); + SAFE_FILE_SCANF(PATH_KSM "pages_skipped", "%d", &pages_skipped_begin); + wait_ksmd_full_scan(); + + tst_res(TINFO, "stop KSM"); + SAFE_FILE_PRINTF(PATH_KSM "run", "0"); + + SAFE_FILE_SCANF(PATH_KSM "full_scans", "%d", &full_scans_end); + SAFE_FILE_SCANF(PATH_KSM "pages_skipped", "%d", &pages_skipped_end); + diff_pages = pages_skipped_end - pages_skipped_begin; + diff_scans = full_scans_end - full_scans_begin; + + if (diff_pages < diff_scans * 50 / 100) { + tst_res(TINFO, "number of pages %d", diff_pages); + tst_res(TINFO, "number of scans %d", diff_scans); + tst_res(TFAIL, "not enough pages have been skipped by smart_scan"); + } else { + tst_res(TPASS, "smart_scan skipped more than 50%% of the pages"); + } + +#ifdef HAVE_DECL_MADV_MERGEABLE + if (madvise(memory, page_size, MADV_UNMERGEABLE) == -1) + tst_brk(TBROK|TERRNO, "madvise"); +#endif +} + +static struct tst_test test = { + .needs_root = 1, + .options = (struct tst_option[]) { + {} + }, + .save_restore = (const struct tst_path_val[]) { + {PATH_KSM "pages_skipped", NULL, TST_SR_TCONF}, + {PATH_KSM "run", NULL, TST_SR_TCONF}, + {PATH_KSM "sleep_millisecs", NULL, TST_SR_TCONF}, + {PATH_KSM "smart_scan", "1", + TST_SR_SKIP_MISSING | TST_SR_TCONF}, + {} + }, + .needs_kconfigs = (const char *const[]){ + "CONFIG_KSM=y", + NULL + }, + .test_all = verify_ksm, +}; diff --git a/testcases/kernel/mem/ksm/ksm_common.h b/testcases/kernel/mem/ksm/ksm_common.h index a582891c..d677b224 100755 --- a/testcases/kernel/mem/ksm/ksm_common.h +++ b/testcases/kernel/mem/ksm/ksm_common.h @@ -1,32 +1,72 @@ -// SPDX-License-Identifier: GPL-2.0-or-later +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ - /* - * Parse the ksm0* test options in funcion parse_ksm_options(). - */ +/* + * Parse the ksm0* test options in funcion parse_ksm_options(). + */ + +#ifndef KSM_COMMON_H__ +#define KSM_COMMON_H__ #include "tst_test.h" +#include "ksm_helper.h" +#include "numa_helper.h" +#include "ksm_test.h" #define DEFAULT_MEMSIZE 128 -int size = DEFAULT_MEMSIZE, num = 3, unit = 1; -char *opt_sizestr, *opt_numstr, *opt_unitstr; +static int size = DEFAULT_MEMSIZE, num = 3, unit = 1; +static char *opt_sizestr, *opt_numstr, *opt_unitstr; static inline void parse_ksm_options(char *str_size, int *size, char *str_num, int *num, char *str_unit, int *unit) { - if(tst_parse_int(str_size, size, 1, INT_MAX)) + if (tst_parse_int(str_size, size, 1, INT_MAX)) tst_brk(TBROK, "Invalid size '%s'", str_size); - if(tst_parse_int(str_num, num, 3, INT_MAX)) + if (tst_parse_int(str_num, num, 3, INT_MAX)) tst_brk(TBROK, "Invalid num '%s'", str_num); - if(tst_parse_int(str_unit, unit, 1, *size)) + if (tst_parse_int(str_unit, unit, 1, *size)) tst_brk(TBROK, "Invalid unit '%s'", str_unit); + if (*size % *unit != 0) - tst_brk(TBROK, - "the remainder of division of size by unit is " - "not zero."); + tst_brk(TBROK, "the remainder of division of size by unit is not zero."); } + +/* Warning: *DO NOT* use this function in child */ +static inline unsigned int get_a_numa_node(void) +{ + unsigned int nd1, nd2; + int ret; + + ret = get_allowed_nodes(0, 2, &nd1, &nd2); + switch (ret) { + case 0: + break; + case -3: + tst_brk(TCONF, "requires a NUMA system."); + default: + tst_brk(TBROK | TERRNO, "1st get_allowed_nodes"); + } + + ret = get_allowed_nodes(NH_MEMS | NH_CPUS, 1, &nd1); + switch (ret) { + case 0: + tst_res(TINFO, "get node%u.", nd1); + return nd1; + case -3: + tst_brk(TCONF, "requires a NUMA system that has " + "at least one node with both memory and CPU " + "available."); + default: + tst_brk(TBROK | TERRNO, "2nd get_allowed_nodes"); + } + + /* not reached */ + abort(); +} + +#endif /* KSM_COMMON_H__ */ diff --git a/testcases/kernel/mem/ksm/ksm_test.h b/testcases/kernel/mem/ksm/ksm_test.h new file mode 100644 index 00000000..cbad147d --- /dev/null +++ b/testcases/kernel/mem/ksm/ksm_test.h @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Linux Test Project, 2011-2021 + * Copyright (c) Cyril Hrubis 2024 + */ +#ifndef KSM_TEST_ +#define KSM_TEST_ + +#include + +static inline void check(char *path, long int value) +{ + char fullpath[BUFSIZ]; + long actual_val; + + snprintf(fullpath, BUFSIZ, PATH_KSM "%s", path); + SAFE_FILE_SCANF(fullpath, "%ld", &actual_val); + + if (actual_val != value) + tst_res(TFAIL, "%s is not %ld but %ld.", path, value, + actual_val); + else + tst_res(TPASS, "%s is %ld.", path, actual_val); +} + +static inline void final_group_check(int run, int pages_shared, int pages_sharing, + int pages_volatile, int pages_unshared, + int sleep_millisecs, int pages_to_scan) +{ + int ksm_run_orig; + + tst_res(TINFO, "check!"); + check("run", run); + + /* + * Temporarily stop the KSM scan during the checks: during the + * KSM scan the rmap_items in the stale unstable tree of the + * old pass are removed from it and are later reinserted in + * the new unstable tree of the current pass. So if the checks + * run in the race window between removal and re-insertion, it + * can lead to unexpected false positives where page_volatile + * is elevated and page_unshared is recessed. + */ + SAFE_FILE_SCANF(PATH_KSM "run", "%d", &ksm_run_orig); + SAFE_FILE_PRINTF(PATH_KSM "run", "0"); + + check("pages_shared", pages_shared); + check("pages_sharing", pages_sharing); + check("pages_volatile", pages_volatile); + check("pages_unshared", pages_unshared); + check("sleep_millisecs", sleep_millisecs); + check("pages_to_scan", pages_to_scan); + + SAFE_FILE_PRINTF(PATH_KSM "run", "%d", ksm_run_orig); +} + +static inline void ksm_group_check(int run, int pages_shared, int pages_sharing, + int pages_volatile, int pages_unshared, + int sleep_millisecs, int pages_to_scan) +{ + if (run != 1) { + tst_res(TFAIL, "group_check run is not 1, %d.", run); + } else { + /* wait for ksm daemon to scan all mergeable pages. */ + wait_ksmd_full_scan(); + } + + final_group_check(run, pages_shared, pages_sharing, + pages_volatile, pages_unshared, + sleep_millisecs, pages_to_scan); +} + +static inline void verify(char **memory, char value, int proc, + int start, int end, int start2, int end2) +{ + int i, j; + + tst_res(TINFO, "child %d verifies memory content.", proc); + + for (j = start; j < end; j++) + for (i = start2; i < end2; i++) + if (memory[j][i] != value) + tst_res(TFAIL, "child %d has %c at " + "%d,%d,%d.", + proc, memory[j][i], proc, j, i); +} + +struct ksm_merge_data { + char data; + unsigned int mergeable_size; +}; + +static inline void ksm_child_memset(int child_num, unsigned int size, + unsigned int total_unit, + struct ksm_merge_data ksm_merge_data, + char **memory) +{ + unsigned int i = 0, j; + unsigned int unit = size / total_unit; + + tst_res(TINFO, "child %d continues...", child_num); + + if (ksm_merge_data.mergeable_size == size * TST_MB) { + tst_res(TINFO, "child %d allocates %u MB filled with '%c'", + child_num, size, ksm_merge_data.data); + + } else { + tst_res(TINFO, "child %d allocates %u MB filled with '%c'" + " except one page with 'e'", + child_num, size, ksm_merge_data.data); + } + + for (j = 0; j < total_unit; j++) { + for (i = 0; (unsigned int)i < unit * TST_MB; i++) + memory[j][i] = ksm_merge_data.data; + } + + /* if it contains unshared page, then set 'e' char + * at the end of the last page + */ + if (ksm_merge_data.mergeable_size < size * TST_MB) + memory[j-1][i-1] = 'e'; +} + +static inline void create_ksm_child(int child_num, unsigned int size, + unsigned int unit, + struct ksm_merge_data *ksm_merge_data) +{ + unsigned int j, total_unit; + char **memory; + + /* The total units in all */ + total_unit = size / unit; + + /* Apply for the space for memory */ + memory = SAFE_MALLOC(total_unit * sizeof(char *)); + for (j = 0; j < total_unit; j++) { + memory[j] = SAFE_MMAP(NULL, unit * TST_MB, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); +#ifdef HAVE_DECL_MADV_MERGEABLE + if (madvise(memory[j], unit * TST_MB, MADV_MERGEABLE) == -1) + tst_brk(TBROK|TERRNO, "madvise"); +#endif + } + + tst_res(TINFO, "child %d stops.", child_num); + if (raise(SIGSTOP) == -1) + tst_brk(TBROK|TERRNO, "kill"); + fflush(stdout); + + for (j = 0; j < 4; j++) { + + ksm_child_memset(child_num, size, total_unit, + ksm_merge_data[j], memory); + + fflush(stdout); + + tst_res(TINFO, "child %d stops.", child_num); + if (raise(SIGSTOP) == -1) + tst_brk(TBROK|TERRNO, "kill"); + + if (ksm_merge_data[j].mergeable_size < size * TST_MB) { + verify(memory, 'e', child_num, total_unit - 1, + total_unit, unit * TST_MB - 1, unit * TST_MB); + verify(memory, ksm_merge_data[j].data, child_num, + 0, total_unit, 0, unit * TST_MB - 1); + } else { + verify(memory, ksm_merge_data[j].data, child_num, + 0, total_unit, 0, unit * TST_MB); + } + } + + tst_res(TINFO, "child %d finished.", child_num); +} + +static inline void stop_ksm_children(int *child, int num) +{ + int k, status; + + tst_res(TINFO, "wait for all children to stop."); + for (k = 0; k < num; k++) { + SAFE_WAITPID(child[k], &status, WUNTRACED); + if (!WIFSTOPPED(status)) + tst_brk(TBROK, "child %d was not stopped", k); + } +} + +static inline void resume_ksm_children(int *child, int num) +{ + int k; + + tst_res(TINFO, "resume all children."); + for (k = 0; k < num; k++) + SAFE_KILL(child[k], SIGCONT); + + fflush(stdout); +} + +static inline void create_same_memory(unsigned int size, int num, unsigned int unit) +{ + int i, j, status, *child; + unsigned long ps, pages; + struct ksm_merge_data **ksm_data; + + struct ksm_merge_data ksm_data0[] = { + {'c', size*TST_MB}, {'c', size*TST_MB}, {'d', size*TST_MB}, {'d', size*TST_MB}, + }; + struct ksm_merge_data ksm_data1[] = { + {'a', size*TST_MB}, {'b', size*TST_MB}, {'d', size*TST_MB}, {'d', size*TST_MB-1}, + }; + struct ksm_merge_data ksm_data2[] = { + {'a', size*TST_MB}, {'a', size*TST_MB}, {'d', size*TST_MB}, {'d', size*TST_MB}, + }; + + ps = sysconf(_SC_PAGE_SIZE); + pages = TST_MB / ps; + + ksm_data = malloc((num - 3) * sizeof(struct ksm_merge_data *)); + /* Since from third child, the data is same with the first child's */ + for (i = 0; i < num - 3; i++) { + ksm_data[i] = malloc(4 * sizeof(struct ksm_merge_data)); + for (j = 0; j < 4; j++) { + ksm_data[i][j].data = ksm_data0[j].data; + ksm_data[i][j].mergeable_size = + ksm_data0[j].mergeable_size; + } + } + + child = SAFE_MALLOC(num * sizeof(int)); + + for (i = 0; i < num; i++) { + fflush(stdout); + switch (child[i] = SAFE_FORK()) { + case 0: + if (i == 0) { + create_ksm_child(i, size, unit, ksm_data0); + exit(0); + } else if (i == 1) { + create_ksm_child(i, size, unit, ksm_data1); + exit(0); + } else if (i == 2) { + create_ksm_child(i, size, unit, ksm_data2); + exit(0); + } else { + create_ksm_child(i, size, unit, ksm_data[i-3]); + exit(0); + } + } + } + + stop_ksm_children(child, num); + + tst_res(TINFO, "KSM merging..."); + if (access(PATH_KSM "max_page_sharing", F_OK) == 0) { + SAFE_FILE_PRINTF(PATH_KSM "run", "2"); + SAFE_FILE_PRINTF(PATH_KSM "max_page_sharing", "%ld", size * pages * num); + } + + SAFE_FILE_PRINTF(PATH_KSM "run", "1"); + SAFE_FILE_PRINTF(PATH_KSM "pages_to_scan", "%ld", size * pages * num); + SAFE_FILE_PRINTF(PATH_KSM "sleep_millisecs", "0"); + + resume_ksm_children(child, num); + stop_ksm_children(child, num); + ksm_group_check(1, 2, size * num * pages - 2, 0, 0, 0, size * pages * num); + + resume_ksm_children(child, num); + stop_ksm_children(child, num); + ksm_group_check(1, 3, size * num * pages - 3, 0, 0, 0, size * pages * num); + + resume_ksm_children(child, num); + stop_ksm_children(child, num); + ksm_group_check(1, 1, size * num * pages - 1, 0, 0, 0, size * pages * num); + + resume_ksm_children(child, num); + stop_ksm_children(child, num); + ksm_group_check(1, 1, size * num * pages - 2, 0, 1, 0, size * pages * num); + + tst_res(TINFO, "KSM unmerging..."); + SAFE_FILE_PRINTF(PATH_KSM "run", "2"); + + resume_ksm_children(child, num); + final_group_check(2, 0, 0, 0, 0, 0, size * pages * num); + + tst_res(TINFO, "stop KSM."); + SAFE_FILE_PRINTF(PATH_KSM "run", "0"); + final_group_check(0, 0, 0, 0, 0, 0, size * pages * num); + + while (waitpid(-1, &status, 0) > 0) + if (WEXITSTATUS(status) != 0) + tst_res(TFAIL, "child exit status is %d", + WEXITSTATUS(status)); +} + +#endif /* KSM_TEST_ */ diff --git a/testcases/kernel/mem/lib/Makefile b/testcases/kernel/mem/lib/Makefile deleted file mode 100755 index d4624e9b..00000000 --- a/testcases/kernel/mem/lib/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (C) 2010 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See -# the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# - -top_srcdir ?= ../../../.. - -include $(top_srcdir)/include/mk/env_pre.mk - -CPPFLAGS += -I$(abs_srcdir)/../include -INTERNAL_LIB := libmem.a - -include $(top_srcdir)/testcases/kernel/include/lib.mk -include $(top_srcdir)/include/mk/lib.mk diff --git a/testcases/kernel/mem/lib/mem.c b/testcases/kernel/mem/lib/mem.c deleted file mode 100755 index fbfeef02..00000000 --- a/testcases/kernel/mem/lib/mem.c +++ /dev/null @@ -1,711 +0,0 @@ -#define TST_NO_DEFAULT_MAIN - -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_NUMA_H -#include -#endif -#if HAVE_NUMAIF_H -#include -#endif -#include -#include -#include -#include -#include -#include - -#include "mem.h" -#include "numa_helper.h" - -/* OOM */ - -static int alloc_mem(long int length, int testcase) -{ - char *s; - long i, pagesz = getpagesize(); - int loop = 10; - - tst_res(TINFO, "thread (%lx), allocating %ld bytes.", - (unsigned long) pthread_self(), length); - - s = mmap(NULL, length, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (s == MAP_FAILED) - return errno; - - if (testcase == MLOCK) { - while (mlock(s, length) == -1 && loop > 0) { - if (EAGAIN != errno) - return errno; - usleep(300000); - loop--; - } - } - -#ifdef HAVE_DECL_MADV_MERGEABLE - if (testcase == KSM && madvise(s, length, MADV_MERGEABLE) == -1) - return errno; -#endif - for (i = 0; i < length; i += pagesz) - s[i] = '\a'; - - return 0; -} - -static void *child_alloc_thread(void *args) -{ - int ret = 0; - - /* keep allocating until there's an error */ - while (!ret) - ret = alloc_mem(LENGTH, (long)args); - exit(ret); -} - -static void child_alloc(int testcase, int lite, int threads) -{ - int i; - pthread_t *th; - - if (lite) { - int ret = alloc_mem(TESTMEM * 2 + MB, testcase); - exit(ret); - } - - th = malloc(sizeof(pthread_t) * threads); - if (!th) { - tst_res(TINFO | TERRNO, "malloc"); - goto out; - } - - for (i = 0; i < threads; i++) { - TEST(pthread_create(&th[i], NULL, child_alloc_thread, - (void *)((long)testcase))); - if (TST_RET) { - tst_res(TINFO | TRERRNO, "pthread_create"); - /* - * Keep going if thread other than first fails to - * spawn due to lack of resources. - */ - if (i == 0 || TST_RET != EAGAIN) - goto out; - } - } - - /* wait for one of threads to exit whole process */ - while (1) - sleep(1); -out: - exit(1); -} - -/* - * oom - allocates memory according to specified testcase and checks - * desired outcome (e.g. child killed, operation failed with ENOMEM) - * @testcase: selects how child allocates memory - * valid choices are: NORMAL, MLOCK and KSM - * @lite: if non-zero, child makes only single TESTMEM+MB allocation - * if zero, child keeps allocating memory until it gets killed - * or some operation fails - * @retcode: expected return code of child process - * if matches child ret code, this function reports PASS, - * otherwise it reports FAIL - * @allow_sigkill: if zero and child is killed, this function reports FAIL - * if non-zero, then if child is killed by SIGKILL - * it is considered as PASS - */ -void oom(int testcase, int lite, int retcode, int allow_sigkill) -{ - pid_t pid; - int status, threads; - - tst_enable_oom_protection(0); - - switch (pid = SAFE_FORK()) { - case 0: - tst_disable_oom_protection(0); - threads = MAX(1, tst_ncpus() - 1); - child_alloc(testcase, lite, threads); - default: - break; - } - - tst_res(TINFO, "expected victim is %d.", pid); - SAFE_WAITPID(-1, &status, 0); - - if (WIFSIGNALED(status)) { - if (allow_sigkill && WTERMSIG(status) == SIGKILL) { - tst_res(TPASS, "victim signalled: (%d) %s", - SIGKILL, - tst_strsig(SIGKILL)); - } else { - tst_res(TFAIL, "victim signalled: (%d) %s", - WTERMSIG(status), - tst_strsig(WTERMSIG(status))); - } - } else if (WIFEXITED(status)) { - if (WEXITSTATUS(status) == retcode) { - tst_res(TPASS, "victim retcode: (%d) %s", - retcode, strerror(retcode)); - } else { - tst_res(TFAIL, "victim unexpectedly ended with " - "retcode: %d, expected: %d", - WEXITSTATUS(status), retcode); - } - } else { - tst_res(TFAIL, "victim unexpectedly ended"); - } -} - -#ifdef HAVE_NUMA_V2 -static void set_global_mempolicy(int mempolicy) -{ - unsigned long nmask[MAXNODES / BITS_PER_LONG] = { 0 }; - int num_nodes, *nodes; - int ret; - - if (mempolicy) { - ret = get_allowed_nodes_arr(NH_MEMS|NH_CPUS, &num_nodes, &nodes); - if (ret != 0) - tst_brk(TBROK|TERRNO, "get_allowed_nodes_arr"); - if (num_nodes < 2) { - tst_res(TINFO, "mempolicy need NUMA system support"); - free(nodes); - return; - } - switch(mempolicy) { - case MPOL_BIND: - /* bind the second node */ - set_node(nmask, nodes[1]); - break; - case MPOL_INTERLEAVE: - case MPOL_PREFERRED: - if (num_nodes == 2) { - tst_res(TINFO, "The mempolicy need " - "more than 2 numa nodes"); - free(nodes); - return; - } else { - /* Using the 2nd,3rd node */ - set_node(nmask, nodes[1]); - set_node(nmask, nodes[2]); - } - break; - default: - tst_brk(TBROK|TERRNO, "Bad mempolicy mode"); - } - if (set_mempolicy(mempolicy, nmask, MAXNODES) == -1) - tst_brk(TBROK|TERRNO, "set_mempolicy"); - } -} -#else -static void set_global_mempolicy(int mempolicy LTP_ATTRIBUTE_UNUSED) { } -#endif - -void testoom(int mempolicy, int lite, int retcode, int allow_sigkill) -{ - int ksm_run_orig; - - set_global_mempolicy(mempolicy); - - tst_res(TINFO, "start normal OOM testing."); - oom(NORMAL, lite, retcode, allow_sigkill); - - tst_res(TINFO, "start OOM testing for mlocked pages."); - oom(MLOCK, lite, retcode, allow_sigkill); - - /* - * Skip oom(KSM) if lite == 1, since limit_in_bytes may vary from - * run to run, which isn't reliable for oom03 cgroup test. - */ - if (access(PATH_KSM, F_OK) == -1 || lite == 1) { - tst_res(TINFO, "KSM is not configed or lite == 1, " - "skip OOM test for KSM pags"); - } else { - tst_res(TINFO, "start OOM testing for KSM pages."); - SAFE_FILE_SCANF(PATH_KSM "run", "%d", &ksm_run_orig); - SAFE_FILE_PRINTF(PATH_KSM "run", "1"); - oom(KSM, lite, retcode, allow_sigkill); - SAFE_FILE_PRINTF(PATH_KSM "run", "%d", ksm_run_orig); - } -} - -/* KSM */ - -static void check(char *path, long int value) -{ - char fullpath[BUFSIZ]; - long actual_val; - - snprintf(fullpath, BUFSIZ, PATH_KSM "%s", path); - SAFE_FILE_SCANF(fullpath, "%ld", &actual_val); - - if (actual_val != value) - tst_res(TFAIL, "%s is not %ld but %ld.", path, value, - actual_val); - else - tst_res(TPASS, "%s is %ld.", path, actual_val); -} - -static void final_group_check(int run, int pages_shared, int pages_sharing, - int pages_volatile, int pages_unshared, - int sleep_millisecs, int pages_to_scan) -{ - int ksm_run_orig; - - tst_res(TINFO, "check!"); - check("run", run); - - /* - * Temporarily stop the KSM scan during the checks: during the - * KSM scan the rmap_items in the stale unstable tree of the - * old pass are removed from it and are later reinserted in - * the new unstable tree of the current pass. So if the checks - * run in the race window between removal and re-insertion, it - * can lead to unexpected false positives where page_volatile - * is elevated and page_unshared is recessed. - */ - SAFE_FILE_SCANF(PATH_KSM "run", "%d", &ksm_run_orig); - SAFE_FILE_PRINTF(PATH_KSM "run", "0"); - - check("pages_shared", pages_shared); - check("pages_sharing", pages_sharing); - check("pages_volatile", pages_volatile); - check("pages_unshared", pages_unshared); - check("sleep_millisecs", sleep_millisecs); - check("pages_to_scan", pages_to_scan); - - SAFE_FILE_PRINTF(PATH_KSM "run", "%d", ksm_run_orig); -} - -void ksm_group_check(int run, int pages_shared, int pages_sharing, - int pages_volatile, int pages_unshared, - int sleep_millisecs, int pages_to_scan) -{ - if (run != 1) { - tst_res(TFAIL, "group_check run is not 1, %d.", run); - } else { - /* wait for ksm daemon to scan all mergeable pages. */ - wait_ksmd_full_scan(); - } - - final_group_check(run, pages_shared, pages_sharing, - pages_volatile, pages_unshared, - sleep_millisecs, pages_to_scan); -} - -static void verify(char **memory, char value, int proc, - int start, int end, int start2, int end2) -{ - int i, j; - void *s = NULL; - - s = SAFE_MALLOC((end - start) * (end2 - start2)); - - tst_res(TINFO, "child %d verifies memory content.", proc); - memset(s, value, (end - start) * (end2 - start2)); - if (memcmp(memory[start], s, (end - start) * (end2 - start2)) - != 0) - for (j = start; j < end; j++) - for (i = start2; i < end2; i++) - if (memory[j][i] != value) - tst_res(TFAIL, "child %d has %c at " - "%d,%d,%d.", - proc, memory[j][i], proc, - j, i); - free(s); -} - -void check_hugepage(void) -{ - if (access(PATH_HUGEPAGES, F_OK)) - tst_brk(TCONF, "Huge page is not supported."); -} - -struct ksm_merge_data { - char data; - unsigned int mergeable_size; -}; - -static void ksm_child_memset(int child_num, int size, int total_unit, - struct ksm_merge_data ksm_merge_data, char **memory) -{ - int i = 0, j; - int unit = size / total_unit; - - tst_res(TINFO, "child %d continues...", child_num); - - if (ksm_merge_data.mergeable_size == size * MB) { - tst_res(TINFO, "child %d allocates %d MB filled with '%c'", - child_num, size, ksm_merge_data.data); - - } else { - tst_res(TINFO, "child %d allocates %d MB filled with '%c'" - " except one page with 'e'", - child_num, size, ksm_merge_data.data); - } - - for (j = 0; j < total_unit; j++) { - for (i = 0; (unsigned int)i < unit * MB; i++) - memory[j][i] = ksm_merge_data.data; - } - - /* if it contains unshared page, then set 'e' char - * at the end of the last page - */ - if (ksm_merge_data.mergeable_size < size * MB) - memory[j-1][i-1] = 'e'; -} - -static void create_ksm_child(int child_num, int size, int unit, - struct ksm_merge_data *ksm_merge_data) -{ - int j, total_unit; - char **memory; - - /* The total units in all */ - total_unit = size / unit; - - /* Apply for the space for memory */ - memory = SAFE_MALLOC(total_unit * sizeof(char *)); - for (j = 0; j < total_unit; j++) { - memory[j] = SAFE_MMAP(NULL, unit * MB, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); -#ifdef HAVE_DECL_MADV_MERGEABLE - if (madvise(memory[j], unit * MB, MADV_MERGEABLE) == -1) - tst_brk(TBROK|TERRNO, "madvise"); -#endif - } - - tst_res(TINFO, "child %d stops.", child_num); - if (raise(SIGSTOP) == -1) - tst_brk(TBROK|TERRNO, "kill"); - fflush(stdout); - - for (j = 0; j < 4; j++) { - - ksm_child_memset(child_num, size, total_unit, - ksm_merge_data[j], memory); - - fflush(stdout); - - tst_res(TINFO, "child %d stops.", child_num); - if (raise(SIGSTOP) == -1) - tst_brk(TBROK|TERRNO, "kill"); - - if (ksm_merge_data[j].mergeable_size < size * MB) { - verify(memory, 'e', child_num, total_unit - 1, - total_unit, unit * MB - 1, unit * MB); - verify(memory, ksm_merge_data[j].data, child_num, - 0, total_unit, 0, unit * MB - 1); - } else { - verify(memory, ksm_merge_data[j].data, child_num, - 0, total_unit, 0, unit * MB); - } - } - - tst_res(TINFO, "child %d finished.", child_num); -} - -static void stop_ksm_children(int *child, int num) -{ - int k, status; - - tst_res(TINFO, "wait for all children to stop."); - for (k = 0; k < num; k++) { - SAFE_WAITPID(child[k], &status, WUNTRACED); - if (!WIFSTOPPED(status)) - tst_brk(TBROK, "child %d was not stopped", k); - } -} - -static void resume_ksm_children(int *child, int num) -{ - int k; - - tst_res(TINFO, "resume all children."); - for (k = 0; k < num; k++) - SAFE_KILL(child[k], SIGCONT); - - fflush(stdout); -} - -void create_same_memory(int size, int num, int unit) -{ - int i, j, status, *child; - unsigned long ps, pages; - struct ksm_merge_data **ksm_data; - - struct ksm_merge_data ksm_data0[] = { - {'c', size*MB}, {'c', size*MB}, {'d', size*MB}, {'d', size*MB}, - }; - struct ksm_merge_data ksm_data1[] = { - {'a', size*MB}, {'b', size*MB}, {'d', size*MB}, {'d', size*MB-1}, - }; - struct ksm_merge_data ksm_data2[] = { - {'a', size*MB}, {'a', size*MB}, {'d', size*MB}, {'d', size*MB}, - }; - - ps = sysconf(_SC_PAGE_SIZE); - pages = MB / ps; - - ksm_data = malloc((num - 3) * sizeof(struct ksm_merge_data *)); - /* Since from third child, the data is same with the first child's */ - for (i = 0; i < num - 3; i++) { - ksm_data[i] = malloc(4 * sizeof(struct ksm_merge_data)); - for (j = 0; j < 4; j++) { - ksm_data[i][j].data = ksm_data0[j].data; - ksm_data[i][j].mergeable_size = - ksm_data0[j].mergeable_size; - } - } - - child = SAFE_MALLOC(num * sizeof(int)); - - for (i = 0; i < num; i++) { - fflush(stdout); - switch (child[i] = SAFE_FORK()) { - case 0: - if (i == 0) { - create_ksm_child(i, size, unit, ksm_data0); - exit(0); - } else if (i == 1) { - create_ksm_child(i, size, unit, ksm_data1); - exit(0); - } else if (i == 2) { - create_ksm_child(i, size, unit, ksm_data2); - exit(0); - } else { - create_ksm_child(i, size, unit, ksm_data[i-3]); - exit(0); - } - } - } - - stop_ksm_children(child, num); - - tst_res(TINFO, "KSM merging..."); - if (access(PATH_KSM "max_page_sharing", F_OK) == 0) { - SAFE_FILE_PRINTF(PATH_KSM "run", "2"); - SAFE_FILE_PRINTF(PATH_KSM "max_page_sharing", "%ld", size * pages * num); - } - - SAFE_FILE_PRINTF(PATH_KSM "run", "1"); - SAFE_FILE_PRINTF(PATH_KSM "pages_to_scan", "%ld", size * pages * num); - SAFE_FILE_PRINTF(PATH_KSM "sleep_millisecs", "0"); - - resume_ksm_children(child, num); - stop_ksm_children(child, num); - ksm_group_check(1, 2, size * num * pages - 2, 0, 0, 0, size * pages * num); - - resume_ksm_children(child, num); - stop_ksm_children(child, num); - ksm_group_check(1, 3, size * num * pages - 3, 0, 0, 0, size * pages * num); - - resume_ksm_children(child, num); - stop_ksm_children(child, num); - ksm_group_check(1, 1, size * num * pages - 1, 0, 0, 0, size * pages * num); - - resume_ksm_children(child, num); - stop_ksm_children(child, num); - ksm_group_check(1, 1, size * num * pages - 2, 0, 1, 0, size * pages * num); - - tst_res(TINFO, "KSM unmerging..."); - SAFE_FILE_PRINTF(PATH_KSM "run", "2"); - - resume_ksm_children(child, num); - final_group_check(2, 0, 0, 0, 0, 0, size * pages * num); - - tst_res(TINFO, "stop KSM."); - SAFE_FILE_PRINTF(PATH_KSM "run", "0"); - final_group_check(0, 0, 0, 0, 0, 0, size * pages * num); - - while (waitpid(-1, &status, 0) > 0) - if (WEXITSTATUS(status) != 0) - tst_res(TFAIL, "child exit status is %d", - WEXITSTATUS(status)); -} - -/* THP */ - -/* cpuset/memcg */ -static void gather_node_cpus(char *cpus, long nd) -{ - int ncpus = 0; - int i; - long online; - char buf[BUFSIZ]; - char path[BUFSIZ], path1[BUFSIZ]; - - while (path_exist(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus)) - ncpus++; - - for (i = 0; i < ncpus; i++) { - snprintf(path, BUFSIZ, - PATH_SYS_SYSTEM "/node/node%ld/cpu%d", nd, i); - if (path_exist(path)) { - snprintf(path1, BUFSIZ, "%s/online", path); - /* - * if there is no online knob, then the cpu cannot - * be taken offline - */ - if (path_exist(path1)) { - SAFE_FILE_SCANF(path1, "%ld", &online); - if (online == 0) - continue; - } - sprintf(buf, "%d,", i); - strcat(cpus, buf); - } - } - /* Remove the trailing comma. */ - cpus[strlen(cpus) - 1] = '\0'; -} - -void write_cpusets(const struct tst_cg_group *cg, long nd) -{ - char cpus[BUFSIZ] = ""; - - SAFE_CG_PRINTF(cg, "cpuset.mems", "%ld", nd); - - gather_node_cpus(cpus, nd); - /* - * If the 'nd' node doesn't contain any CPUs, - * the first ID of CPU '0' will be used as - * the value of cpuset.cpus. - */ - if (strlen(cpus) != 0) { - SAFE_CG_PRINT(cg, "cpuset.cpus", cpus); - } else { - tst_res(TINFO, "No CPUs in the node%ld; " - "using only CPU0", nd); - SAFE_CG_PRINT(cg, "cpuset.cpus", "0"); - } -} - -/* shared */ - -/* Warning: *DO NOT* use this function in child */ -unsigned int get_a_numa_node(void) -{ - unsigned int nd1, nd2; - int ret; - - ret = get_allowed_nodes(0, 2, &nd1, &nd2); - switch (ret) { - case 0: - break; - case -3: - tst_brk(TCONF, "requires a NUMA system."); - default: - tst_brk(TBROK | TERRNO, "1st get_allowed_nodes"); - } - - ret = get_allowed_nodes(NH_MEMS | NH_CPUS, 1, &nd1); - switch (ret) { - case 0: - tst_res(TINFO, "get node%u.", nd1); - return nd1; - case -3: - tst_brk(TCONF, "requires a NUMA system that has " - "at least one node with both memory and CPU " - "available."); - default: - tst_brk(TBROK | TERRNO, "2nd get_allowed_nodes"); - } - - /* not reached */ - abort(); -} - -int path_exist(const char *path, ...) -{ - va_list ap; - char pathbuf[PATH_MAX]; - - va_start(ap, path); - vsnprintf(pathbuf, sizeof(pathbuf), path, ap); - va_end(ap); - - return access(pathbuf, F_OK) == 0; -} - -void set_sys_tune(char *sys_file, long tune, int check) -{ - long val; - char path[BUFSIZ]; - - tst_res(TINFO, "set %s to %ld", sys_file, tune); - - snprintf(path, BUFSIZ, PATH_SYSVM "%s", sys_file); - SAFE_FILE_PRINTF(path, "%ld", tune); - - if (check) { - val = get_sys_tune(sys_file); - if (val != tune) - tst_brk(TBROK, "%s = %ld, but expect %ld", - sys_file, val, tune); - } -} - -long get_sys_tune(char *sys_file) -{ - char path[BUFSIZ]; - long tune; - - snprintf(path, BUFSIZ, PATH_SYSVM "%s", sys_file); - SAFE_FILE_SCANF(path, "%ld", &tune); - - return tune; -} - -void update_shm_size(size_t * shm_size) -{ - size_t shmmax; - - SAFE_FILE_SCANF(PATH_SHMMAX, "%zu", &shmmax); - if (*shm_size > shmmax) { - tst_res(TINFO, "Set shm_size to shmmax: %zu", shmmax); - *shm_size = shmmax; - } -} - -int range_is_mapped(unsigned long low, unsigned long high) -{ - FILE *fp; - - fp = fopen("/proc/self/maps", "r"); - if (fp == NULL) - tst_brk(TBROK | TERRNO, "Failed to open /proc/self/maps."); - - while (!feof(fp)) { - unsigned long start, end; - int ret; - - ret = fscanf(fp, "%lx-%lx %*[^\n]\n", &start, &end); - if (ret != 2) { - fclose(fp); - tst_brk(TBROK | TERRNO, "Couldn't parse /proc/self/maps line."); - } - - if ((start >= low) && (start < high)) { - fclose(fp); - return 1; - } - if ((end >= low) && (end < high)) { - fclose(fp); - return 1; - } - } - - fclose(fp); - return 0; -} diff --git a/testcases/kernel/mem/mmapstress/mmapstress01.c b/testcases/kernel/mem/mmapstress/mmapstress01.c index 6209c1bc..bafdfbaa 100755 --- a/testcases/kernel/mem/mmapstress/mmapstress01.c +++ b/testcases/kernel/mem/mmapstress/mmapstress01.c @@ -6,7 +6,6 @@ * 10/03/2022 Refactor to LTP framework edliaw@google.com */ /*\ - * [Description] * This test stresses mmaps, without dealing with fragments or anything! * It forks a specified number of children, * all of whom mmap the same file, make a given number of accesses @@ -159,7 +158,7 @@ static void child_mapper(char *file, unsigned int procno, unsigned int nprocs) nloops = (randloops) ? (lrand48() % MAXLOOPS) : MAXLOOPS; if (debug) - tst_res(TINFO, "child %d (pid %d): seed %d, fsize %lld, mapsize %ld, off %lld, loop %d", + tst_res(TINFO, "child %u (pid %d): seed %d, fsize %lld, mapsize %ld, off %lld, loop %d", procno, getpid(), seed, (long long)filesize, (long)mapsize, (long long)offset / pagesize, nloops); @@ -179,7 +178,7 @@ static void child_mapper(char *file, unsigned int procno, unsigned int nprocs) for (i = procno; i < validsize; i += nprocs) { if (*((unsigned char *)(paddr + i)) != ((procno + pattern) & 0xff)) - tst_brk(TFAIL, "child %d: invalid data \n" + tst_brk(TFAIL, "child %u: invalid data \n" " at pg %d off %d, exp ", procno, *((unsigned char *)(paddr + i)), randpage, i, (procno + pattern) & 0xff); @@ -187,13 +186,13 @@ static void child_mapper(char *file, unsigned int procno, unsigned int nprocs) *(paddr + i) = (procno + pattern) & 0xff; } } + if (do_sync) { randpage = lrand48() % mappages; paddr = maddr + (randpage * pagesize); /* page address */ - if (msync(paddr, (mappages - randpage) * pagesize, - MS_SYNC) == -1) - tst_brk(TBROK | TERRNO, "msync failed"); + SAFE_MSYNC(paddr, (mappages - randpage) * pagesize, MS_SYNC); } + SAFE_MUNMAP(maddr, mapsize); exit(0); } @@ -362,7 +361,7 @@ static struct tst_test test = { {}, }, .cleanup = cleanup, - .max_runtime = 12, + .runtime = 12, .needs_tmpdir = 1, .forks_child = 1, }; diff --git a/testcases/kernel/mem/mtest01/mtest01.c b/testcases/kernel/mem/mtest01/mtest01.c index fb991ce8..04fde959 100755 --- a/testcases/kernel/mem/mtest01/mtest01.c +++ b/testcases/kernel/mem/mtest01/mtest01.c @@ -235,7 +235,7 @@ static struct tst_test test = { {"v", &verbose, "Verbose"}, {} }, - .max_runtime = 300, + .runtime = 300, .setup = setup, .cleanup = cleanup, .test_all = mem_test, diff --git a/testcases/kernel/mem/mtest06/mmap1.c b/testcases/kernel/mem/mtest06/mmap1.c index 907597d1..5c4ffa66 100755 --- a/testcases/kernel/mem/mtest06/mmap1.c +++ b/testcases/kernel/mem/mtest06/mmap1.c @@ -55,7 +55,7 @@ static unsigned long data_matched; static unsigned long repeated_reads; /* sequence id for each map/unmap performed */ -static int mapcnt, unmapcnt; +static tst_atomic_t mapcnt, unmapcnt; /* stored sequence id before making read attempt */ static int br_map, br_unmap; @@ -249,6 +249,6 @@ static void run(void) static struct tst_test test = { .test_all = run, .setup = setup, - .max_runtime = 180, + .runtime = 180, .needs_tmpdir = 1, }; diff --git a/testcases/kernel/mem/mtest06/mmap3.c b/testcases/kernel/mem/mtest06/mmap3.c index 19f4e33d..58127ad9 100755 --- a/testcases/kernel/mem/mtest06/mmap3.c +++ b/testcases/kernel/mem/mtest06/mmap3.c @@ -28,7 +28,7 @@ static int loops = 1000; static int threads = 40; static volatile int sig_caught; -static int threads_running; +static tst_atomic_t threads_running; static int mkfile(int *size) { @@ -182,5 +182,5 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .test_all = test_mmap, - .max_runtime = 60, + .runtime = 60, }; diff --git a/testcases/kernel/mem/mtest07/mallocstress.c b/testcases/kernel/mem/mtest07/mallocstress.c index 2d047cf6..f5b4f301 100755 --- a/testcases/kernel/mem/mtest07/mallocstress.c +++ b/testcases/kernel/mem/mtest07/mallocstress.c @@ -194,7 +194,7 @@ static void cleanup(void) } static struct tst_test test = { - .max_runtime = 600, + .runtime = 600, .needs_checkpoints = 1, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/mem/oom/Makefile b/testcases/kernel/mem/oom/Makefile index dd55fb8d..db621b9c 100755 --- a/testcases/kernel/mem/oom/Makefile +++ b/testcases/kernel/mem/oom/Makefile @@ -19,5 +19,10 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk + +include $(top_srcdir)/testcases/kernel/include/lib.mk + +CFLAGS+=-pthread +LDFLAGS+=-pthread + include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/mem/oom/oom.h b/testcases/kernel/mem/oom/oom.h new file mode 100644 index 00000000..41cc681f --- /dev/null +++ b/testcases/kernel/mem/oom/oom.h @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Linux Test Project, 2011-2021 + * Copyright (c) Cyril Hrubis 2024 + */ +#ifndef OOM_H_ +#define OOM_H_ + +#include +#include "config.h" +#include "numa_helper.h" + +#define PATH_KSM "/sys/kernel/mm/ksm/" + +#define LENGTH (3UL<<30) +#define NORMAL 1 +#define MLOCK 2 +#define KSM 3 + +#ifdef HAVE_NUMA_V2 +static inline void set_global_mempolicy(int mempolicy) +{ + unsigned long nmask[MAXNODES / BITS_PER_LONG] = { 0 }; + int num_nodes, *nodes; + int ret; + + if (mempolicy) { + ret = get_allowed_nodes_arr(NH_MEMS|NH_CPUS, &num_nodes, &nodes); + if (ret != 0) + tst_brk(TBROK|TERRNO, "get_allowed_nodes_arr"); + if (num_nodes < 2) { + tst_res(TINFO, "mempolicy need NUMA system support"); + free(nodes); + return; + } + switch(mempolicy) { + case MPOL_BIND: + /* bind the second node */ + set_node(nmask, nodes[1]); + break; + case MPOL_INTERLEAVE: + case MPOL_PREFERRED: + if (num_nodes == 2) { + tst_res(TINFO, "The mempolicy need " + "more than 2 numa nodes"); + free(nodes); + return; + } else { + /* Using the 2nd,3rd node */ + set_node(nmask, nodes[1]); + set_node(nmask, nodes[2]); + } + break; + default: + tst_brk(TBROK|TERRNO, "Bad mempolicy mode"); + } + if (set_mempolicy(mempolicy, nmask, MAXNODES) == -1) + tst_brk(TBROK|TERRNO, "set_mempolicy"); + } +} +#else +static void set_global_mempolicy(int mempolicy LTP_ATTRIBUTE_UNUSED) { } +#endif + +static int alloc_mem(long int length, int testcase) +{ + char *s; + long i, pagesz = getpagesize(); + int loop = 10; + + tst_res(TINFO, "thread (%lx), allocating %ld bytes.", + (unsigned long) pthread_self(), length); + + s = mmap(NULL, length, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (s == MAP_FAILED) + return errno; + + if (testcase == MLOCK) { + while (mlock(s, length) == -1 && loop > 0) { + if (EAGAIN != errno) + return errno; + usleep(300000); + loop--; + } + } + +#ifdef HAVE_DECL_MADV_MERGEABLE + if (testcase == KSM && madvise(s, length, MADV_MERGEABLE) == -1) + return errno; +#endif + for (i = 0; i < length; i += pagesz) + s[i] = '\a'; + + return 0; +} + +static void *child_alloc_thread(void *args) +{ + int ret = 0; + + /* keep allocating until there's an error */ + while (!ret) + ret = alloc_mem(LENGTH, (long)args); + exit(ret); +} + +static void child_alloc(int testcase, int lite, int threads) +{ + int i; + pthread_t *th; + + if (lite) { + int ret = alloc_mem(TESTMEM * 2 + TST_MB, testcase); + exit(ret); + } + + th = malloc(sizeof(pthread_t) * threads); + if (!th) { + tst_res(TINFO | TERRNO, "malloc"); + goto out; + } + + for (i = 0; i < threads; i++) { + TEST(pthread_create(&th[i], NULL, child_alloc_thread, + (void *)((long)testcase))); + if (TST_RET) { + tst_res(TINFO | TRERRNO, "pthread_create"); + /* + * Keep going if thread other than first fails to + * spawn due to lack of resources. + */ + if (i == 0 || TST_RET != EAGAIN) + goto out; + } + } + + /* wait for one of threads to exit whole process */ + while (1) + sleep(1); +out: + exit(1); +} + +/* + * oom - allocates memory according to specified testcase and checks + * desired outcome (e.g. child killed, operation failed with ENOMEM) + * @testcase: selects how child allocates memory + * valid choices are: NORMAL, MLOCK and KSM + * @lite: if non-zero, child makes only single TESTMEM+TST_MB allocation + * if zero, child keeps allocating memory until it gets killed + * or some operation fails + * @retcode: expected return code of child process + * if matches child ret code, this function reports PASS, + * otherwise it reports FAIL + * @allow_sigkill: if zero and child is killed, this function reports FAIL + * if non-zero, then if child is killed by SIGKILL + * it is considered as PASS + */ +static inline void oom(int testcase, int lite, int retcode, int allow_sigkill) +{ + pid_t pid; + int status, threads; + + tst_enable_oom_protection(0); + + switch (pid = SAFE_FORK()) { + case 0: + tst_disable_oom_protection(0); + threads = MAX(1, tst_ncpus() - 1); + child_alloc(testcase, lite, threads); + default: + break; + } + + tst_res(TINFO, "expected victim is %d.", pid); + SAFE_WAITPID(-1, &status, 0); + + if (WIFSIGNALED(status)) { + if (allow_sigkill && WTERMSIG(status) == SIGKILL) { + tst_res(TPASS, "victim signalled: (%d) %s", + SIGKILL, + tst_strsig(SIGKILL)); + } else { + tst_res(TFAIL, "victim signalled: (%d) %s", + WTERMSIG(status), + tst_strsig(WTERMSIG(status))); + } + } else if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == retcode) { + tst_res(TPASS, "victim retcode: (%d) %s", + retcode, strerror(retcode)); + } else { + tst_res(TFAIL, "victim unexpectedly ended with " + "retcode: %d, expected: %d", + WEXITSTATUS(status), retcode); + } + } else { + tst_res(TFAIL, "victim unexpectedly ended"); + } +} + +static inline void testoom(int mempolicy, int lite, int retcode, int allow_sigkill) +{ + int ksm_run_orig; + + set_global_mempolicy(mempolicy); + + tst_res(TINFO, "start normal OOM testing."); + oom(NORMAL, lite, retcode, allow_sigkill); + + tst_res(TINFO, "start OOM testing for mlocked pages."); + oom(MLOCK, lite, retcode, allow_sigkill); + + /* + * Skip oom(KSM) if lite == 1, since limit_in_bytes may vary from + * run to run, which isn't reliable for oom03 cgroup test. + */ + if (access(PATH_KSM, F_OK) == -1 || lite == 1) { + tst_res(TINFO, "KSM is not configed or lite == 1, " + "skip OOM test for KSM pags"); + } else { + tst_res(TINFO, "start OOM testing for KSM pages."); + SAFE_FILE_SCANF(PATH_KSM "run", "%d", &ksm_run_orig); + SAFE_FILE_PRINTF(PATH_KSM "run", "1"); + oom(KSM, lite, retcode, allow_sigkill); + SAFE_FILE_PRINTF(PATH_KSM "run", "%d", ksm_run_orig); + } +} + +#endif /* OOM_H_ */ diff --git a/testcases/kernel/mem/oom/oom01.c b/testcases/kernel/mem/oom/oom01.c index b1369989..48fe8584 100755 --- a/testcases/kernel/mem/oom/oom01.c +++ b/testcases/kernel/mem/oom/oom01.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Out Of Memory (OOM) - * - * The program is designed to cope with unpredictable like amount and - * system physical memory, swap size and other VMM technology like KSM, - * memcg, memory hotplug and so on which may affect the OOM - * behaviours. It simply increase the memory consumption 3G each time - * until all the available memory is consumed and OOM is triggered. - * * Copyright (C) 2010-2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. + * Copyright (c) Linux Test Project, 2011-2023 + */ +/*\ + * Out Of Memory (OOM) test */ #include @@ -27,35 +14,34 @@ #include #include #include -#include "lapi/abisize.h" -#include "mem.h" +#include "tst_test.h" +#include "oom.h" + +#define OVERCOMMIT_MEMORY "/proc/sys/vm/overcommit_memory" static void verify_oom(void) { -#ifdef TST_ABI32 - tst_brk(TCONF, "test is not designed for 32-bit system."); -#endif - /* we expect mmap to fail before OOM is hit */ - set_sys_tune("overcommit_memory", 2, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_MEMORY, 2, 1); oom(NORMAL, 0, ENOMEM, 0); /* with overcommit_memory set to 0 or 1 there's no * guarantee that mmap fails before OOM */ - set_sys_tune("overcommit_memory", 0, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_MEMORY, 0, 1); oom(NORMAL, 0, ENOMEM, 1); - set_sys_tune("overcommit_memory", 1, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_MEMORY, 1, 1); testoom(0, 0, ENOMEM, 1); } static struct tst_test test = { .needs_root = 1, .forks_child = 1, - .max_runtime = TST_UNLIMITED_RUNTIME, + .timeout = TST_UNLIMITED_TIMEOUT, .test_all = verify_oom, + .skip_in_compat = 1, .save_restore = (const struct tst_path_val[]) { - {"/proc/sys/vm/overcommit_memory", NULL, TST_SR_TBROK}, + {OVERCOMMIT_MEMORY, NULL, TST_SR_TBROK}, {} }, }; diff --git a/testcases/kernel/mem/oom/oom02.c b/testcases/kernel/mem/oom/oom02.c index 8d565d12..01a405ec 100755 --- a/testcases/kernel/mem/oom/oom02.c +++ b/testcases/kernel/mem/oom/oom02.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Out Of Memory (OOM) for mempolicy - need NUMA system support - * - * The program is designed to cope with unpredictable like amount and - * system physical memory, swap size and other VMM technology like KSM, - * memcg, memory hotplug and so on which may affect the OOM - * behaviours. It simply increase the memory consumption 3G each time - * until all the available memory is consumed and OOM is triggered. - * * Copyright (C) 2010-2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. + * Copyright (c) Linux Test Project, 2011-2023 + */ +/*\ + * Out Of Memory (OOM) test for mempolicy - need NUMA system support */ #include "config.h" @@ -27,21 +14,17 @@ #include #include #if HAVE_NUMA_H -#include +# include #endif -#include "lapi/abisize.h" +#include "tst_test.h" #include "numa_helper.h" -#include "mem.h" +#include "oom.h" #ifdef HAVE_NUMA_V2 static void verify_oom(void) { -#ifdef TST_ABI32 - tst_brk(TCONF, "test is not designed for 32-bit system."); -#endif - tst_res(TINFO, "OOM on MPOL_BIND mempolicy..."); testoom(MPOL_BIND, 0, ENOMEM, 1); @@ -61,9 +44,10 @@ static void setup(void) static struct tst_test test = { .needs_root = 1, .forks_child = 1, - .max_runtime = TST_UNLIMITED_RUNTIME, + .timeout = TST_UNLIMITED_TIMEOUT, .setup = setup, .test_all = verify_oom, + .skip_in_compat = 1, .save_restore = (const struct tst_path_val[]) { {"/proc/sys/vm/overcommit_memory", "1", TST_SR_TBROK}, {} diff --git a/testcases/kernel/mem/oom/oom03.c b/testcases/kernel/mem/oom/oom03.c index 778055d0..f218b813 100755 --- a/testcases/kernel/mem/oom/oom03.c +++ b/testcases/kernel/mem/oom/oom03.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Out Of Memory (OOM) for Memory Resource Controller - * - * The program is designed to cope with unpredictable like amount and - * system physical memory, swap size and other VMM technology like KSM, - * memcg, memory hotplug and so on which may affect the OOM - * behaviours. It simply increase the memory consumption 3G each time - * until all the available memory is consumed and OOM is triggered. - * * Copyright (C) 2010-2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. + * Copyright (c) Linux Test Project, 2011-2023 + */ +/*\ + * Out Of Memory (OOM) test for Memory Resource Controller */ #include "config.h" @@ -27,20 +14,17 @@ #include #include #if HAVE_NUMA_H -#include +# include #endif -#include "lapi/abisize.h" +#include "tst_test.h" #include "numa_helper.h" -#include "mem.h" +#include "oom.h" #ifdef HAVE_NUMA_V2 static void verify_oom(void) { -#ifdef TST_ABI32 - tst_brk(TCONF, "test is not designed for 32-bit system."); -#endif testoom(0, 0, ENOMEM, 1); if (SAFE_CG_HAS(tst_cg, "memory.swap.max")) { @@ -53,12 +37,12 @@ static void verify_oom(void) * * To get more opportunities to reach the swap limitation, * let's scale down the value of 'memory.swap.max' to only - * 1MB for CGroup v2. + * 1TST_MB for CGroup v2. */ if (!TST_CG_VER_IS_V1(tst_cg, "memory")) - SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", MB); + SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", TST_MB); else - SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", TESTMEM + MB); + SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", TESTMEM + TST_MB); testoom(0, 1, ENOMEM, 1); @@ -86,10 +70,11 @@ static void setup(void) static struct tst_test test = { .needs_root = 1, .forks_child = 1, - .max_runtime = TST_UNLIMITED_RUNTIME, + .timeout = TST_UNLIMITED_TIMEOUT, .setup = setup, .test_all = verify_oom, .needs_cgroup_ctrls = (const char *const []){ "memory", NULL }, + .skip_in_compat = 1, .save_restore = (const struct tst_path_val[]) { {"/proc/sys/vm/overcommit_memory", "1", TST_SR_TBROK}, {} diff --git a/testcases/kernel/mem/oom/oom04.c b/testcases/kernel/mem/oom/oom04.c index d27d9d9e..d4beb12c 100755 --- a/testcases/kernel/mem/oom/oom04.c +++ b/testcases/kernel/mem/oom/oom04.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Out Of Memory (OOM) for CPUSET - * - * The program is designed to cope with unpredictable like amount and - * system physical memory, swap size and other VMM technology like KSM, - * memcg, memory hotplug and so on which may affect the OOM - * behaviours. It simply increase the memory consumption 3G each time - * until all the available memory is consumed and OOM is triggered. - * * Copyright (C) 2010-2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. + * Copyright (c) Linux Test Project, 2011-2023 + */ +/*\ + * Out Of Memory (OOM) test for CPUSET */ #include "config.h" @@ -27,20 +14,17 @@ #include #include #if HAVE_NUMA_H -#include +# include #endif -#include "lapi/abisize.h" +#include "tst_test.h" #include "numa_helper.h" -#include "mem.h" +#include "oom.h" #ifdef HAVE_NUMA_V2 static void verify_oom(void) { -#ifdef TST_ABI32 - tst_brk(TCONF, "test is not designed for 32-bit system."); -#endif tst_res(TINFO, "OOM on CPUSET..."); testoom(0, 0, ENOMEM, 1); @@ -75,17 +59,18 @@ static void setup(void) if (ret < 0) tst_brk(TBROK, "Failed to get a memory node " "using get_allowed_nodes()"); - write_cpusets(tst_cg, memnode); + write_node_cpusets(tst_cg, memnode); SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid()); } static struct tst_test test = { .needs_root = 1, .forks_child = 1, - .max_runtime = TST_UNLIMITED_RUNTIME, + .timeout = TST_UNLIMITED_TIMEOUT, .setup = setup, .test_all = verify_oom, .needs_cgroup_ctrls = (const char *const []){ "cpuset", NULL }, + .skip_in_compat = 1, .save_restore = (const struct tst_path_val[]) { {"/proc/sys/vm/overcommit_memory", "1", TST_SR_TBROK}, {} diff --git a/testcases/kernel/mem/oom/oom05.c b/testcases/kernel/mem/oom/oom05.c index eb1a6426..6b8d436d 100755 --- a/testcases/kernel/mem/oom/oom05.c +++ b/testcases/kernel/mem/oom/oom05.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Out Of Memory (OOM) for MEMCG and CPUSET - * - * The program is designed to cope with unpredictable like amount and - * system physical memory, swap size and other VMM technology like KSM, - * memcg, memory hotplug and so on which may affect the OOM - * behaviours. It simply increase the memory consumption 3G each time - * until all the available memory is consumed and OOM is triggered. - * * Copyright (C) 2013-2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. + * Copyright (c) Linux Test Project, 2014-2023 + */ +/*\ + * Out Of Memory (OOM) test for MEMCG and CPUSET */ #include "config.h" @@ -27,21 +14,17 @@ #include #include #if HAVE_NUMA_H -#include +# include #endif -#include "lapi/abisize.h" +#include "tst_test.h" #include "numa_helper.h" -#include "mem.h" +#include "oom.h" #ifdef HAVE_NUMA_V2 static void verify_oom(void) { -#ifdef TST_ABI32 - tst_brk(TCONF, "test is not designed for 32-bit system."); -#endif - tst_res(TINFO, "OOM on CPUSET & MEMCG..."); testoom(0, 0, ENOMEM, 1); @@ -62,9 +45,9 @@ static void verify_oom(void) tst_res(TINFO, "OOM on CPUSET & MEMCG with " "special memswap limitation:"); if (!TST_CG_VER_IS_V1(tst_cg, "memory")) - SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", MB); + SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", TST_MB); else - SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", TESTMEM + MB); + SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%lu", TESTMEM + TST_MB); testoom(0, 1, ENOMEM, 1); @@ -96,7 +79,7 @@ void setup(void) tst_brk(TBROK, "Failed to get a memory node " "using get_allowed_nodes()"); - write_cpusets(tst_cg, memnode); + write_node_cpusets(tst_cg, memnode); SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid()); SAFE_CG_PRINTF(tst_cg, "memory.max", "%lu", TESTMEM); } @@ -104,12 +87,13 @@ void setup(void) static struct tst_test test = { .needs_root = 1, .forks_child = 1, - .max_runtime = TST_UNLIMITED_RUNTIME, + .timeout = TST_UNLIMITED_TIMEOUT, .setup = setup, .test_all = verify_oom, .needs_cgroup_ctrls = (const char *const []){ "memory", "cpuset", NULL }, + .skip_in_compat = 1, .save_restore = (const struct tst_path_val[]) { {"/proc/sys/vm/overcommit_memory", "1", TST_SR_TBROK}, {} diff --git a/testcases/kernel/mem/shmt/Makefile b/testcases/kernel/mem/shmt/Makefile index ee37fd9a..75249fe6 100755 --- a/testcases/kernel/mem/shmt/Makefile +++ b/testcases/kernel/mem/shmt/Makefile @@ -22,5 +22,8 @@ top_srcdir ?= ../../../.. +LTPLIBS = newipc +LTPLDLIBS = -lltpnewipc + include $(top_srcdir)/include/mk/testcases.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/mem/shmt/shmt02.c b/testcases/kernel/mem/shmt/shmt02.c index b33a042f..de1d0b0c 100755 --- a/testcases/kernel/mem/shmt/shmt02.c +++ b/testcases/kernel/mem/shmt/shmt02.c @@ -1,120 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt02 - * - * CALLS - * shmctl(2) shmget(2) - * - * ALGORITHM +/*\ * Create and attach a shared memory segment, write to it - * and then remove it. Verify that the shared memory segment + * and then remove it. Verify that the shared memory segment * is accessible as long as the process is still alive. - * */ -#include -#include -#include -#include -#include -#include +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "tst_rand_data.h" +#include "libnewipc.h" -/** LTP Port **/ -#include "test.h" +#define SHMSIZE 16 -char *TCID = "shmt02"; /* Test program identifier. */ -int TST_TOTAL = 3; /* Total number of test cases. */ - -/**************/ - -#define K_1 1024 - -static int rm_shm(int); - -int main(void) +static void run(void) { - register int shmid; char *cp; + int shmid; key_t key; - errno = 0; - key = (key_t) getpid(); + key = GETIPCKEY(); -/*----------------------------------------------------------------*/ + if (!SAFE_FORK()) { + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); - if ((shmid = shmget(key, 16 * K_1, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_brkm(TFAIL, NULL, - "shmget Failed: shmid = %d, errno = %d", - shmid, errno); + cp = SAFE_SHMAT(shmid, NULL, 0); + memcpy(cp, tst_rand_data, SHMSIZE); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); + TST_EXP_EQ_LI(memcmp(cp, tst_rand_data, SHMSIZE), 0); + + TST_CHECKPOINT_WAKE(0); + + _exit(0); } - tst_resm(TPASS, "shmget"); + TST_CHECKPOINT_WAIT(0); + tst_reap_children(); -/*----------------------------------------------------------------*/ + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); + cp = SAFE_SHMAT(shmid, NULL, 0); + TST_EXP_EXPR(memcmp(cp, tst_rand_data, SHMSIZE) < 0); - cp = shmat(shmid, NULL, 0); - - if (cp == (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, "shmat Failed: shmid = %d, errno = %d", - shmid, errno); - rm_shm(shmid); - tst_exit(); - } - - *cp = '1'; - *(cp + 1) = '2'; - - tst_resm(TPASS, "shmat"); - -/*----------------------------------------------------------------*/ - - rm_shm(shmid); - - if (*cp != '1' || *(cp + 1) != '2') { - tst_resm(TFAIL, - "Error in shared memory contents: shmid = %d", - shmid); - } - - tst_resm(TPASS, "Correct shared memory contents"); - -/*------------------------------------------------------------------*/ - - tst_exit(); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); } -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d", - shmid, errno); - } - return (0); -} +static struct tst_test test = { + .test_all = run, + .needs_checkpoints = 1, + .forks_child = 1, +}; diff --git a/testcases/kernel/mem/shmt/shmt03.c b/testcases/kernel/mem/shmt/shmt03.c index 08c27ce0..63f5a7b4 100755 --- a/testcases/kernel/mem/shmt/shmt03.c +++ b/testcases/kernel/mem/shmt/shmt03.c @@ -1,133 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt3 - * - * CALLS - * shmctl(2) shmget(2) shmat(2) - * - * ALGORITHM +/*\ * Create one shared memory segment and attach it twice to the same process, * at an address that is chosen by the system. After the first attach has * completed, write to it and then do the second attach. * Verify that the doubly attached segment contains the same data. - * */ -#include -#include -#include -#include -#include +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "tst_rand_data.h" +#include "libnewipc.h" -/** LTP Port **/ -#include "test.h" +#define SHMSIZE 16 -char *TCID = "shmt03"; /* Test program identifier. */ -int TST_TOTAL = 4; /* Total number of test cases. */ -/**************/ - -#define K_1 1024 -#define SUCCESSFUL 1 - -int first_attach, second_attach; -static int rm_shm(int); - -int main(void) +static void run(void) { char *cp1, *cp2; int shmid; key_t key; - key = (key_t) getpid(); - errno = 0; + key = GETIPCKEY(); -/*------------------------------------------------------------*/ + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); - if ((shmid = shmget(key, 16 * K_1, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_brkm(TFAIL, NULL, - "shmget Failed: shmid = %d, errno = %d", - shmid, errno); - } + cp1 = SAFE_SHMAT(shmid, NULL, 0); + memcpy(cp1, tst_rand_data, SHMSIZE); - tst_resm(TPASS, "shmget"); + cp2 = SAFE_SHMAT(shmid, NULL, 0); + TST_EXP_EQ_LI(memcmp(cp2, tst_rand_data, SHMSIZE), 0); -/*------------------------------------------------------------*/ - - if ((cp1 = shmat(shmid, NULL, 0)) == (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, "shmat Failed: shmid = %d, errno = %d", - shmid, errno); - } else { - *cp1 = '1'; - *(cp1 + 5 * K_1) = '2'; - first_attach = SUCCESSFUL; - } - - tst_resm(TPASS, "1st shmat"); - -/*------------------------------------------------------------*/ - - if ((cp2 = shmat(shmid, NULL, 0)) == (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, "shmat Failed: shmid = %d, errno = %d", - shmid, errno); - } else { - second_attach = SUCCESSFUL; - if ((*cp2 != '1' || *(cp2 + 5 * K_1) != '2') && - first_attach == SUCCESSFUL) { - tst_resm(TFAIL, "Error: Shared memory contents"); - } - } - - tst_resm(TPASS, "2nd shmat"); - -/*---------------------------------------------------------------*/ - - rm_shm(shmid); - - if (first_attach && second_attach) { - if (*cp2 != '1' || *(cp2 + 5 * K_1) != '2' || - *cp1 != '1' || *(cp1 + 5 * K_1) != '2') { - tst_resm(TFAIL, "Error: Shared memory contents"); - } - } - - tst_resm(TPASS, "Correct shared memory contents"); -/*-----------------------------------------------------------------*/ - tst_exit(); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); } -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d", - shmid, errno); - } - return (0); -} +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/mem/shmt/shmt04.c b/testcases/kernel/mem/shmt/shmt04.c index c9a654e7..c07b1ef9 100755 --- a/testcases/kernel/mem/shmt/shmt04.c +++ b/testcases/kernel/mem/shmt/shmt04.c @@ -1,197 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 03/21/2003 enable ia64 Jacky.Malcles + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 03/21/2003 enable ia64 Jacky.Malcles */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt04 - * - * CALLS - * shmctl(2) shmget(2) shmat(2) - * - * ALGORITHM +/*\ * Parent process forks a child. Child pauses until parent has created * a shared memory segment, attached to it and written to it too. At that * time child gets the shared memory segment id, attaches to it and * verifies that its contents are the same as the contents of the * parent attached segment. - * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "tst_rand_data.h" +#include "libnewipc.h" -/** LTP Port **/ -#include "test.h" +#define SHMSIZE 16 -char *TCID = "shmt04"; /* Test program identifier. */ -int TST_TOTAL = 2; /* Total number of test cases. */ -/**************/ - -key_t key; -sigset_t set; - -#define SIZE 16*1024 - -int child(); -static int rm_shm(int); - -int main(void) +static void run(void) { - char *cp = NULL; - int pid, pid1, shmid; - int status; - - key = (key_t) getpid(); - - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - sigprocmask(SIG_BLOCK, &set, NULL); - - pid = fork(); - switch (pid) { - case -1: - tst_brkm(TBROK, NULL, "fork failed"); - case 0: - child(); - } - -/*----------------------------------------------------------*/ - - if ((shmid = shmget(key, SIZE, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_resm(TFAIL, "Error: shmget: shmid = %d, errno = %d", - shmid, errno); - /* - * kill the child if parent failed to do the attach - */ - (void)kill(pid, SIGINT); - } else { - cp = shmat(shmid, NULL, 0); - - if (cp == (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, - "Error: shmat: shmid = %d, errno = %d", - shmid, errno); - -/* kill the child if parent failed to do the attch */ - - kill(pid, SIGINT); - -/* remove shared memory segment */ - - rm_shm(shmid); - - tst_exit(); - } - *cp = 'A'; - *(cp + 1) = 'B'; - *(cp + 2) = 'C'; - - kill(pid, SIGUSR1); - while ((pid1 = wait(&status)) < 0 && (errno == EINTR)) ; - if (pid1 != pid) { - tst_resm(TFAIL, "Waited on the wrong child"); - tst_resm(TFAIL, - "Error: wait_status = %d, pid1= %d", status, - pid1); - } - } - - tst_resm(TPASS, "shmget,shmat"); - -/*----------------------------------------------------------*/ - - if (shmdt(cp) < 0) { - tst_resm(TFAIL, "shmdt"); - } - - tst_resm(TPASS, "shmdt"); - -/*----------------------------------------------------------*/ - - rm_shm(shmid); - tst_exit(); -} - -int child(void) -{ - int shmid, chld_pid; char *cp; - int sig; + int shmid; + key_t key; - sigwait(&set, &sig); - chld_pid = getpid(); -/*--------------------------------------------------------*/ + key = GETIPCKEY(); - if ((shmid = shmget(key, SIZE, 0)) < 0) { - perror("shmget:child process"); - tst_resm(TFAIL, - "Error: shmget: errno=%d, shmid=%d, child_pid=%d", - errno, shmid, chld_pid); - } else { - cp = shmat(shmid, NULL, 0); + if (!SAFE_FORK()) { + TST_CHECKPOINT_WAIT(0); - if (cp == (char *)-1) { - perror("shmat:child process"); - tst_resm(TFAIL, - "Error: shmat: errno=%d, shmid=%d, child_pid=%d", - errno, shmid, chld_pid); - } else { - if (*cp != 'A') { - tst_resm(TFAIL, "child: not A"); - } - if (*(cp + 1) != 'B') { - tst_resm(TFAIL, "child: not B"); - } - if (*(cp + 2) != 'C') { - tst_resm(TFAIL, "child: not C"); - } - if (*(cp + 8192) != 0) { - tst_resm(TFAIL, "child: not 0"); - } - } + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); + cp = SAFE_SHMAT(shmid, NULL, 0); + TST_EXP_EQ_LI(memcmp(cp, tst_rand_data, SHMSIZE), 0); + /* + * Attach the segment to a different address + * and verify it's contents again. + */ + cp = SAFE_SHMAT(shmid, NULL, 0); + TST_EXP_EQ_LI(memcmp(cp, tst_rand_data, SHMSIZE), 0); + + _exit(0); } - tst_exit(); + + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); + cp = SAFE_SHMAT(shmid, NULL, 0); + memcpy(cp, tst_rand_data, SHMSIZE); + + TST_CHECKPOINT_WAKE(0); + tst_reap_children(); + + SAFE_SHMCTL(shmid, IPC_RMID, NULL); } -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d", - shmid, errno); - } - return (0); -} +static struct tst_test test = { + .test_all = run, + .needs_checkpoints = 1, + .forks_child = 1, +}; diff --git a/testcases/kernel/mem/shmt/shmt05.c b/testcases/kernel/mem/shmt/shmt05.c index 430db20c..e8b5d9eb 100755 --- a/testcases/kernel/mem/shmt/shmt05.c +++ b/testcases/kernel/mem/shmt/shmt05.c @@ -1,126 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt05 - * - * CALLS - * shmctl(2) shmget(2) shmat(2) - * - * ALGORITHM +/*\ * Create two shared memory segments and attach them to the same process * at two different addresses. The addresses DO BUMP into each other. * The second attach should Fail. - * */ -#include -#include -#include +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "libnewipc.h" #include -#include -#include -#include -/** LTP Port **/ -#include "test.h" +#define SHMSIZE 16 -char *TCID = "shmt05"; /* Test program identifier. */ -int TST_TOTAL = 2; /* Total number of test cases. */ -/**************/ - -key_t key[2]; - -#define SIZE (2*SHMLBA) - -static int rm_shm(int); - -int main(void) +static void run(void) { int shmid, shmid1; - char *cp, *cp1; + char *cp; + key_t key[2]; - srand48((getpid() << 16) + (unsigned)time(NULL)); + key[0] = GETIPCKEY(); + key[1] = GETIPCKEY(); - key[0] = (key_t) lrand48(); - key[1] = (key_t) lrand48(); + shmid = SAFE_SHMGET(key[0], SHMSIZE, IPC_CREAT | 0666); + cp = SAFE_SHMAT(shmid, NULL, 0); - cp = NULL; - cp1 = NULL; + shmid1 = SAFE_SHMGET(key[1], SHMSIZE, IPC_CREAT | 0666); + TST_EXP_FAIL((long)shmat(shmid, cp + (SHMSIZE / 2), 0), EINVAL); -/*--------------------------------------------------------*/ - - if ((shmid = shmget(key[0], SIZE, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_resm(TFAIL, - "Error: shmget: shmid = %d, errno = %d\n", - shmid, errno); - } else { - cp = shmat(shmid, NULL, 0); - - if (cp == (char *)-1) { - tst_resm(TFAIL, "shmat"); - rm_shm(shmid); - } - } - - tst_resm(TPASS, "shmget & shmat"); - -/*--------------------------------------------------------*/ - - if ((shmid1 = shmget(key[1], SIZE, IPC_CREAT | 0666)) < 0) { - perror("shmget2"); - tst_resm(TFAIL, - "Error: shmget: shmid1 = %d, errno = %d\n", - shmid1, errno); - } else { - cp1 = shmat(shmid1, cp + (SIZE / 2), 0); - if (cp1 != (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, - "Error: shmat: shmid1 = %d, addr= %p, errno = %d\n", - shmid1, cp1, errno); - } else { - tst_resm(TPASS, "2nd shmget & shmat"); - } - } - -/*------------------------------------------------------*/ - - rm_shm(shmid); - rm_shm(shmid1); - - tst_exit(); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); + SAFE_SHMCTL(shmid1, IPC_RMID, NULL); } -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d\n", - shmid, errno); - } - return (0); -} +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/mem/shmt/shmt06.c b/testcases/kernel/mem/shmt/shmt06.c deleted file mode 100755 index e88ac45c..00000000 --- a/testcases/kernel/mem/shmt/shmt06.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 - */ - -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt06 - * - * CALLS - * shmctl(2) shmget(2) shmat(2) - * - * ALGORITHM - * Parent process forks a child. Child pauses until parent has created - * a shared memory segment, attached to it and written to it too. At that - * time child gets the shared memory segment id, attaches to it at two - * different addresses than the parents and verifies that their contents - * are the same as the contents of the parent attached segment. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SIZE 16*1024 - -/** LTP Port **/ -#include "test.h" - -char *TCID = "shmt06"; /* Test program identifier. */ -int TST_TOTAL = 2; /* Total number of test cases. */ -/**************/ - -key_t key; -sigset_t set; - -int child(); -static int rm_shm(int); - -int main(void) -{ - char *cp = NULL; - int pid, pid1, shmid; - int status; - - key = (key_t) getpid(); - - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - sigprocmask(SIG_BLOCK, &set, NULL); - - pid = fork(); - switch (pid) { - case -1: - tst_brkm(TBROK, NULL, "fork failed"); - case 0: - child(); - } - -/*------------------------------------------------------*/ - - if ((shmid = shmget(key, SIZE, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_resm(TFAIL, "Error: shmget: shmid = %d, errno = %d", - shmid, errno); - /* - * kill the child if parent failed to do the attach - */ - (void)kill(pid, SIGINT); - } else { - cp = shmat(shmid, NULL, 0); - - if (cp == (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, - "Error: shmat: shmid = %d, errno = %d", - shmid, errno); - - /* kill the child if parent failed to do the attch */ - - kill(pid, SIGINT); - - /* remove shared memory segment */ - - rm_shm(shmid); - - tst_exit(); - } - *cp = 'A'; - *(cp + 1) = 'B'; - *(cp + 2) = 'C'; - - kill(pid, SIGUSR1); - while ((pid1 = wait(&status)) < 0 && (errno == EINTR)) ; - if (pid1 != pid) { - tst_resm(TFAIL, "Waited on the wrong child"); - tst_resm(TFAIL, - "Error: wait_status = %d, pid1= %d", status, - pid1); - } - } - - tst_resm(TPASS, "shmget,shmat"); - -/*---------------------------------------------------------------*/ - - if (shmdt(cp) < 0) { - tst_resm(TFAIL, "shmdt"); - } - - tst_resm(TPASS, "shmdt"); - -/*-------------------------------------------------------------*/ - - rm_shm(shmid); - tst_exit(); -} - -int child(void) -{ - int shmid, chld_pid; - char *cp; - int sig; - - sigwait(&set, &sig); - chld_pid = getpid(); - - if ((shmid = shmget(key, SIZE, 0)) < 0) { - perror("shmget:child process"); - tst_resm(TFAIL, - "Error: shmget: errno=%d, shmid=%d, child_pid=%d", - errno, shmid, chld_pid); - } else { - cp = shmat(shmid, NULL, 0); - - if (cp == (char *)-1) { - perror("shmat:child process"); - tst_resm(TFAIL, - "Error: shmat: errno=%d, shmid=%d, child_pid=%d", - errno, shmid, chld_pid); - } else { - if (*cp != 'A') { - tst_resm(TFAIL, "child: not A"); - } - if (*(cp + 1) != 'B') { - tst_resm(TFAIL, "child: not B"); - } - if (*(cp + 2) != 'C') { - tst_resm(TFAIL, "child: not C"); - } - if (*(cp + 8192) != 0) { - tst_resm(TFAIL, "child: not 0"); - } - } - - /* - * Attach the segment to a different addresse - * and verify it's contents again. - */ - cp = shmat(shmid, NULL, 0); - - if (cp == (char *)-1) { - perror("shmat:child process"); - tst_resm(TFAIL, - "Error: shmat: errno=%d, shmid=%d, child_pid=%d", - errno, shmid, chld_pid); - } else { - if (*cp != 'A') { - tst_resm(TFAIL, "child: not A"); - } - if (*(cp + 1) != 'B') { - tst_resm(TFAIL, "child: not B"); - } - if (*(cp + 2) != 'C') { - tst_resm(TFAIL, "child: not C"); - } - if (*(cp + 8192) != 0) { - tst_resm(TFAIL, "child: not 0"); - } - } - } - tst_exit(); -} - -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d", - shmid, errno); - } - return (0); -} diff --git a/testcases/kernel/mem/shmt/shmt07.c b/testcases/kernel/mem/shmt/shmt07.c index 064343f5..962479e7 100755 --- a/testcases/kernel/mem/shmt/shmt07.c +++ b/testcases/kernel/mem/shmt/shmt07.c @@ -1,131 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt07 - * - * CALLS - * shmctl(2) shmget(2) shmat(2) - * - * ALGORITHM +/*\ * Create and attach a shared memory segment, write to it * and then fork a child. The child verifies that the shared memory segment * that it inherited from the parent contains the same data that was originally * written to it by the parent. - * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "tst_rand_data.h" +#include "libnewipc.h" -#define SIZE 16*1024 +#define SHMSIZE 16 -/** LTP Port **/ -#include "test.h" +#include "tst_test.h" -char *TCID = "shmt07"; /* Test program identifier. */ -int TST_TOTAL = 2; /* Total number of test cases. */ -/**************/ - -int child(); -static int rm_shm(int); - -int main(void) +static void run(void) { - char *cp = NULL; - int shmid, pid, status; + char *cp; + int shmid; key_t key; - key = (key_t) getpid(); + key = GETIPCKEY(); -/*---------------------------------------------------------*/ + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); + cp = SAFE_SHMAT(shmid, NULL, 0); + memcpy(cp, tst_rand_data, SHMSIZE); - errno = 0; - - if ((shmid = shmget(key, SIZE, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_brkm(TFAIL, NULL, - "Error: shmget: shmid = %d, errno = %d", - shmid, errno); - } - cp = shmat(shmid, NULL, 0); - - if (cp == (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, - "Error: shmat: shmid = %d, errno = %d", - shmid, errno); - rm_shm(shmid); - tst_exit(); + if (!SAFE_FORK()) { + TST_EXP_EQ_LI(memcmp(cp, tst_rand_data, SHMSIZE), 0); + _exit(0); } - *cp = '1'; - *(cp + 1) = '2'; - - tst_resm(TPASS, "shmget,shmat"); - -/*-------------------------------------------------------*/ - - pid = fork(); - switch (pid) { - case -1: - tst_brkm(TBROK, NULL, "fork failed"); - - case 0: - if (*cp != '1') { - tst_resm(TFAIL, "Error: not 1"); - } - if (*(cp + 1) != '2') { - tst_resm(TFAIL, "Error: not 2"); - } - tst_exit(); - } - - /* parent */ - while (wait(&status) < 0 && errno == EINTR) ; - - tst_resm(TPASS, "cp & cp+1 correct"); - -/*-----------------------------------------------------------*/ - rm_shm(shmid); - tst_exit(); + SAFE_WAIT(NULL); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); } -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d", - shmid, errno); - } - return (0); -} +static struct tst_test test = { + .test_all = run, + .forks_child = 1, +}; diff --git a/testcases/kernel/mem/shmt/shmt08.c b/testcases/kernel/mem/shmt/shmt08.c index 2c3ae9d5..9fb2f6b2 100755 --- a/testcases/kernel/mem/shmt/shmt08.c +++ b/testcases/kernel/mem/shmt/shmt08.c @@ -1,117 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt08 - * - * CALLS - * shmctl(2) shmget(2) shmat(2) shmdt(2) - * - * ALGORITHM +/*\ * Create a shared memory segment. Attach it twice at an address - * that is provided by the system. Detach the previously attached + * that is provided by the system. Detach the previously attached * segments from the process. - * */ -#include -#include -#include -#include -#include +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "libnewipc.h" -#define K_1 1024 +#define SHMSIZE 16 -/** LTP Port **/ -#include "test.h" - -char *TCID = "shmt08"; /* Test program identifier. */ -int TST_TOTAL = 2; /* Total number of test cases. */ -/**************/ - -key_t key; - -static int rm_shm(int); - -int main(void) +static void run(void) { - char *cp = NULL, *cp1 = NULL; + char *cp, *cp1; int shmid; + key_t key; - key = (key_t) getpid(); - errno = 0; -/*-------------------------------------------------------*/ + key = GETIPCKEY(); - if ((shmid = shmget(key, 24 * K_1, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_brkm(TFAIL, NULL, - "Error: shmget: shmid = %d, errno = %d\n", - shmid, errno); - } + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); + cp = SAFE_SHMAT(shmid, NULL, 0); + cp1 = SAFE_SHMAT(shmid, NULL, 0); - cp = shmat(shmid, NULL, 0); - if (cp == (char *)-1) { - tst_resm(TFAIL, "shmat1 Failed"); - rm_shm(shmid); - tst_exit(); - } + SAFE_SHMDT(cp); + SAFE_SHMDT(cp1); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); - cp1 = shmat(shmid, NULL, 0); - if (cp1 == (char *)-1) { - perror("shmat2"); - rm_shm(shmid); - tst_exit(); - } - - tst_resm(TPASS, "shmget,shmat"); - -/*--------------------------------------------------------*/ - - if (shmdt(cp) < 0) { - perror("shmdt2"); - tst_resm(TFAIL, "shmdt:cp"); - } - - if (shmdt(cp1) < 0) { - perror("shmdt1"); - tst_resm(TFAIL, "shmdt:cp1"); - } - - tst_resm(TPASS, "shmdt"); - -/*---------------------------------------------------------*/ - rm_shm(shmid); - tst_exit(); + tst_res(TPASS, "Attached and detached twice"); } -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d\n", - shmid, errno); - } - return (0); -} +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/mem/shmt/shmt09.c b/testcases/kernel/mem/shmt/shmt09.c index 34724411..fb40498e 100755 --- a/testcases/kernel/mem/shmt/shmt09.c +++ b/testcases/kernel/mem/shmt/shmt09.c @@ -1,199 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt09 - * - * CALLS - * sbrk(2) shmctl(2) shmget(2) - * - * ALGORITHM +/*\ * Create a shared memory segment and attach at the default address. * Increase the size of the data segment. * Attach at an address that is less than the break value: should FAIL. * decrease the size of the data segment. * Attach at 4K past the break value: should SUCCEED. * Sbrk() past the attached segment: should FAIL. - * */ -#include -#include -#include #include -#include -#include -#define K_1 1024 +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "libnewipc.h" +#include "tst_test_macros.h" -#define SHMBYTES (SHMLBA - 1) +#define SHMSIZE 16 +#define SHMBYTES (SHMLBA - 1) #define SHMALIGN(p) (((unsigned long)(p) + SHMBYTES) & ~SHMBYTES) -/** LTP Port **/ -#include "test.h" - -char *TCID = "shmt09"; /* Test program identifier. */ -int TST_TOTAL = 4; /* Total number of test cases. */ -/**************/ - #ifdef __ia64__ -#define INCREMENT 8388608 /* 8Mb */ -#elif defined (__mips__) || defined (__hppa__) || defined (__sparc__) -#define INCREMENT 262144 /* 256Kb */ -#elif defined __sh__ || defined (__arm__) || defined(__aarch64__) -#define INCREMENT 16384 /* 16kb */ +#define INCREMENT 8388608 /* 8Mb */ +#elif defined(__mips__) || defined(__hppa__) || defined(__sparc__) +#define INCREMENT 262144 /* 256Kb */ +#elif defined __sh__ || defined(__arm__) || defined(__aarch64__) +#define INCREMENT 16384 /* 16kb */ #else -#define INCREMENT SHMLBA +#define INCREMENT SHMLBA #endif -static int rm_shm(int); - -int main(void) +static void run(void) { - char *c1 = NULL, *c2 = NULL, *c3 = NULL; void *vp; int shmid; key_t key; - key = (key_t) getpid(); + key = GETIPCKEY(); -/*-----------------------------------------------------------*/ + if ((unsigned long)sbrk(16384) >= (-4095UL)) + tst_brk(TFAIL, "Error: sbrk failed, errno = %d", errno); - if ((unsigned long)sbrk(16384) >= (-4095UL)) { - perror("sbrk"); - tst_brkm(TFAIL, NULL, "Error: sbrk failed, errno = %d", - errno); - } + if ((unsigned long)sbrk(-4097) >= (-4095UL)) + tst_brk(TFAIL, "Error: sbrk failed, errno = %d", errno); - if ((unsigned long)sbrk(-4097) >= (-4095UL)) { - perror("sbrk"); - tst_brkm(TFAIL, NULL, "Error: sbrk failed, errno = %d", - errno); - } + shmid = SAFE_SHMGET(key, 10 * SHMSIZE, IPC_CREAT | 0666); + SAFE_SHMAT(shmid, NULL, 0); - if ((shmid = shmget(key, 10 * K_1, IPC_CREAT | 0666)) < 0) { - perror("shmget"); - tst_brkm(TFAIL, - NULL, - "Error: shmget Failed, shmid = %d, errno = %d", - shmid, errno); - } + if ((unsigned long)sbrk(32 * SHMSIZE) >= (-4095UL)) + tst_brk(TFAIL, "Error: sbrk failed, errno = %d", errno); - c1 = shmat(shmid, NULL, 0); - if (c1 == (char *)-1) { - perror("shmat"); - tst_resm(TFAIL, - "Error: shmat Failed, shmid = %d, errno = %d", - shmid, errno); - rm_shm(shmid); - tst_exit(); - } + vp = (void *)((char *)sbrk(0) - 2 * SHMSIZE); + TST_EXP_FAIL((long)shmat(shmid, vp, 0), EINVAL); - tst_resm(TPASS, "sbrk, sbrk, shmget, shmat"); + if ((unsigned long)sbrk(-16000) >= (-4095UL)) + tst_brk(TFAIL, "Error: sbrk failed, errno = %d", errno); -/*--------------------------------------------------------*/ - - if ((unsigned long)sbrk(32 * K_1) >= (-4095UL)) { - perror("sbrk"); - tst_resm(TFAIL, "Error: sbrk failed, errno = %d", errno); - rm_shm(shmid); - tst_exit(); - } - vp = (void *)((char *)sbrk(0) - 2 * K_1); - c2 = shmat(shmid, vp, 0); - if (c2 != (char *)-1) { - tst_resm(TFAIL, - "ERROR: shmat: succeeded!: shmid = %d, shmaddr = %p, " - "att_addr = %p", shmid, c2, vp); - rm_shm(shmid); - tst_exit(); - } - - tst_resm(TPASS, "sbrk, shmat"); - -/*---------------------------------------------------------*/ - - if ((unsigned long)sbrk(-16000) >= (-4095UL)) { - perror("sbrk"); - tst_resm(TFAIL, "Error: sbrk failed, errno = %d", errno); - rm_shm(shmid); - tst_exit(); - } #ifdef __mips__ - vp = (void *)((char *)sbrk(0) + 256 * K_1); -#elif defined(__powerpc__) || defined(__powerpc64__) + vp = (void *)((char *)sbrk(0) + 256 * SHMSIZE); +#elif defined(__powerpc__) || defined(__powerpc64__) vp = (void *)((char *)sbrk(0) + getpagesize()); #else /* SHM_RND rounds vp on the nearest multiple of SHMLBA */ vp = (void *)SHMALIGN((char *)sbrk(0) + 1); #endif - c3 = shmat(shmid, vp, SHM_RND); - if (c3 == (char *)-1) { - perror("shmat1"); - tst_resm(TFAIL, - "Error: shmat Failed, shmid = %d, errno = %d", - shmid, errno); - rm_shm(shmid); - tst_exit(); - } + SAFE_SHMAT(shmid, vp, SHM_RND); - tst_resm(TPASS, "sbrk, shmat"); + TST_EXP_FAIL((long)sbrk(INCREMENT), ENOMEM); -/*--------------------------------------------------------*/ -#if defined (__ia64__) || defined(__mips__) || defined(__hppa__) || defined(__arm__) || defined(__aarch64__) - while ((vp = sbrk(INCREMENT)) != (void *)-1) ; - if (errno != ENOMEM) { - tst_resm(TFAIL, "Error: sbrk failed, errno = %d", errno); - rm_shm(shmid); - tst_exit(); - } -#else - if ((vp = sbrk(INCREMENT)) != (void *)-1) { - tst_resm(TFAIL, - "Error: sbrk succeeded! ret = %p, curbrk = %p, ", - vp, sbrk(0)); - rm_shm(shmid); - tst_exit(); - } -#endif - - tst_resm(TPASS, "sbrk"); - -/*------------------------------------------------------*/ - - rm_shm(shmid); - tst_exit(); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); } -static int rm_shm(int shmid) -{ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d", - shmid, errno); - } - return (0); -} +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/mem/shmt/shmt10.c b/testcases/kernel/mem/shmt/shmt10.c index c63bb159..2bd48b6b 100755 --- a/testcases/kernel/mem/shmt/shmt10.c +++ b/testcases/kernel/mem/shmt/shmt10.c @@ -1,166 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * 12/20/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* 12/20/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - -/* - * NAME - * shmt10.c - test simultaneous shmat/shmdt - * - * CALLS - * shmget, shmat, shmdt, shmctl - * - * ALGORITHM - * Create a shared memory segment and fork a child. Both - * parent and child spin in a loop attaching and detaching - * the segment. After completing the specified number of - * iterations, the child exits and the parent deletes the - * segment. - * - * USAGE - * shmt10 [-i 500] - * -i # of iterations, default 500 - * +/*\ + * Create a shared memory segment and fork a child. Both + * parent and child spin in a loop attaching and detaching + * the segment. After completing the specified number of + * iterations, the child exits and the parent deletes the + * segment. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "libnewipc.h" -#define SIZE 0x32768 +#define SHMSIZE 0x32768 -/** LTP Port **/ -#include "test.h" +static char *str_iter_n; +static long long iter_n = 500; +static int shmid; -char *TCID = "shmt10"; /* Test program identifier. */ -int TST_TOTAL = 2; /* Total number of test cases. */ -/**************/ - -int shmid; -key_t key; - -static int child(int); -static int rm_shm(int); -static void fini(int); - -int main(int argc, char *argv[]) +static void do_shm_cycle(long long iter_n) { - char *c1 = NULL; - int pid, st; - register int i; - int iter = 500; - int c; - extern char *optarg; + char *addr; - key = (key_t) getpid(); - signal(SIGTERM, fini); - -/*--------------------------------------------------------*/ - - while ((c = getopt(argc, argv, "i:")) != EOF) { - switch (c) { - case 'i': - iter = atoi(optarg); - break; - default: - tst_brkm(TCONF, NULL, "usage: %s [-i <# iterations>]", - argv[0]); - } + for (int i = 0; i < iter_n; i++) { + addr = SAFE_SHMAT(shmid, NULL, 0); + addr[0] = 'a'; + SAFE_SHMDT(addr); } - -/*------------------------------------------------------------------------*/ - - if ((shmid = shmget(key, SIZE, IPC_CREAT | 0666)) < 0) { - tst_resm(TFAIL, "shmget"); - tst_brkm(TFAIL, NULL, "Error: shmid = %d", shmid); - } - - pid = fork(); - switch (pid) { - case -1: - tst_brkm(TBROK, NULL, "fork failed"); - case 0: - child(iter); - tst_exit(); - } - - for (i = 0; i < iter; i++) { - if ((c1 = shmat(shmid, NULL, 0)) == (char *)-1) { - tst_resm(TFAIL, - "Error shmat: iter %d, shmid = %d", i, - shmid); - break; - } - if (shmdt(c1) < 0) { - tst_resm(TFAIL, "Error: shmdt: iter %d ", i); - break; - } - } - while (wait(&st) < 0 && errno == EINTR) ; - tst_resm(TPASS, "shmat,shmdt"); -/*------------------------------------------------------------------------*/ - - rm_shm(shmid); - tst_exit(); } -static int rm_shm(int shmid) +static void run(void) { - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - perror("shmctl"); - tst_brkm(TFAIL, - NULL, - "shmctl Failed to remove: shmid = %d, errno = %d", - shmid, errno); + key_t key; + + key = GETIPCKEY(); + + shmid = SAFE_SHMGET(key, SHMSIZE, IPC_CREAT | 0666); + if (!SAFE_FORK()) { + do_shm_cycle(iter_n); + _exit(0); } - return (0); + + do_shm_cycle(iter_n); + tst_reap_children(); + SAFE_SHMCTL(shmid, IPC_RMID, NULL); + + tst_res(TPASS, "Attached and detached %lld times", iter_n * 2); } -static int child(int iter) +static void setup(void) { - register int i; - char *c1; - - for (i = 0; i < iter; i++) { - if ((c1 = shmat(shmid, NULL, 0)) == (char *)-1) { - tst_brkm(TFAIL, - NULL, - "Error:child proc: shmat: iter %d, shmid = %d", - i, shmid); - } - if (shmdt(c1) < 0) { - tst_brkm(TFAIL, - NULL, "Error: child proc: shmdt: iter %d ", - i); - } - } - return (0); + if (tst_parse_filesize(str_iter_n, &iter_n, 1, LLONG_MAX)) + tst_brk(TBROK, "Invalid amount of iterations: %s", str_iter_n); } -static void fini(int sig) -{ - rm_shm(shmid); -} +static struct tst_test test = { + .setup = setup, + .test_all = run, + .forks_child = 1, + .options = (struct tst_option[]) { + {"n:", &str_iter_n, "Amount of iterations (default 500)"}, + {} + }, +}; diff --git a/testcases/kernel/mem/swapping/Makefile b/testcases/kernel/mem/swapping/Makefile index dd55fb8d..d7ddac44 100755 --- a/testcases/kernel/mem/swapping/Makefile +++ b/testcases/kernel/mem/swapping/Makefile @@ -19,5 +19,4 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/mem/swapping/swapping01.c b/testcases/kernel/mem/swapping/swapping01.c index fc225e4a..c773f222 100755 --- a/testcases/kernel/mem/swapping/swapping01.c +++ b/testcases/kernel/mem/swapping/swapping01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Detect heavy swapping during first time swap use. * * This case is used for testing kernel commit: @@ -40,14 +38,14 @@ #include #include #include +#include "tst_test.h" #include "tst_safe_stdio.h" -#include "lapi/abisize.h" -#include "mem.h" /* allow swapping 1 * phy_mem in maximum */ #define COE_DELTA 1 /* will try to alloc 1.3 * phy_mem */ #define COE_SLIGHT_OVER 0.3 +#define MEM_SIZE 1024 * 1024 static void init_meminfo(void); static void do_alloc(int allow_raise); @@ -62,9 +60,6 @@ static unsigned int start_runtime; static void test_swapping(void) { -#ifdef TST_ABI32 - tst_brk(TCONF, "test is not designed for 32-bit system."); -#endif FILE *file; char line[PATH_MAX]; @@ -83,7 +78,9 @@ static void test_swapping(void) switch (pid = SAFE_FORK()) { case 0: + TST_PRINT_MEMINFO(); do_alloc(0); + TST_PRINT_MEMINFO(); do_alloc(1); exit(0); default: @@ -103,6 +100,13 @@ static void init_meminfo(void) swap_free_init, mem_over_max); } +static void memset_blocks(char *ptr, int mem_count, int sleep_time_ms) { + for (int i = 0; i < mem_count / 1024; i++) { + memset(ptr + (i * MEM_SIZE), 1, MEM_SIZE); + usleep(sleep_time_ms * 1000); + } +} + static void do_alloc(int allow_raise) { long mem_count; @@ -111,15 +115,19 @@ static void do_alloc(int allow_raise) if (allow_raise == 1) tst_res(TINFO, "available physical memory: %ld MB", mem_available_init / 1024); + mem_count = mem_available_init + mem_over; + if (allow_raise == 1) tst_res(TINFO, "try to allocate: %ld MB", mem_count / 1024); s = SAFE_MALLOC(mem_count * 1024); - memset(s, 1, mem_count * 1024); + memset_blocks(s, mem_count, 1); + if ((allow_raise == 1) && (raise(SIGSTOP) == -1)) { tst_res(TINFO, "memory allocated: %ld MB", mem_count / 1024); tst_brk(TBROK | TERRNO, "kill"); } + free(s); } @@ -138,6 +146,7 @@ static void check_swapping(void) swap_free_now = SAFE_READ_MEMINFO("SwapFree:"); sleep(1); long diff = labs(swap_free_now - SAFE_READ_MEMINFO("SwapFree:")); + if (diff < 10) break; @@ -146,9 +155,10 @@ static void check_swapping(void) swapped = SAFE_READ_PROC_STATUS(pid, "VmSwap:"); if (swapped > mem_over_max) { + TST_PRINT_MEMINFO(); kill(pid, SIGCONT); - tst_brk(TFAIL, "heavy swapping detected: " - "%ld MB swapped.", swapped / 1024); + tst_brk(TFAIL, "heavy swapping detected: %ld MB swapped", + swapped / 1024); } tst_res(TPASS, "no heavy swapping detected, %ld MB swapped.", @@ -162,8 +172,9 @@ static struct tst_test test = { .needs_root = 1, .forks_child = 1, .min_mem_avail = 10, - .max_runtime = 600, + .runtime = 600, .test_all = test_swapping, + .skip_in_compat = 1, .needs_kconfigs = (const char *[]) { "CONFIG_SWAP=y", NULL diff --git a/testcases/kernel/mem/thp/Makefile b/testcases/kernel/mem/thp/Makefile index e95712ea..d89ea1dd 100755 --- a/testcases/kernel/mem/thp/Makefile +++ b/testcases/kernel/mem/thp/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. thp04: LDLIBS += -lrt +thp04: CFLAGS += -pthread include $(top_srcdir)/include/mk/testcases.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/mem/thp/thp.h b/testcases/kernel/mem/thp/thp.h new file mode 100644 index 00000000..7723bedc --- /dev/null +++ b/testcases/kernel/mem/thp/thp.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Linux Test Project, 2011-2021 + * Copyright (c) Cyril Hrubis 2024 + */ +#ifndef THP_H +#define THP_H + +#define PATH_THP "/sys/kernel/mm/transparent_hugepage/" + +static inline void check_hugepage(void) +{ + if (access(PATH_HUGEPAGES, F_OK)) + tst_brk(TCONF, "Huge page is not supported."); +} + +#endif /* THP_H */ diff --git a/testcases/kernel/mem/thp/thp01.c b/testcases/kernel/mem/thp/thp01.c index 69825b0f..91c08cb1 100755 --- a/testcases/kernel/mem/thp/thp01.c +++ b/testcases/kernel/mem/thp/thp01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This is a reproducer of CVE-2011-0999, which fixed by mainline commit * a7d6e4ecdb76 ("thp: prevent hugepages during args/env copying into the user stack") * @@ -38,7 +36,6 @@ #include #include #include "tst_test.h" -#include "mem.h" #include "tst_minmax.h" #define ARGS_SZ (256 * 32) diff --git a/testcases/kernel/mem/thp/thp02.c b/testcases/kernel/mem/thp/thp02.c index 56568d1d..4bfe37a2 100755 --- a/testcases/kernel/mem/thp/thp02.c +++ b/testcases/kernel/mem/thp/thp02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test for detecting mremap bug when THP is enabled. * * There was a bug in mremap THP support, sometimes causing crash @@ -38,7 +36,8 @@ #include #include #include -#include "mem.h" +#include "tst_test.h" +#include "thp.h" static int ps; static long hps, size; diff --git a/testcases/kernel/mem/thp/thp03.c b/testcases/kernel/mem/thp/thp03.c index 839efcb0..e8d22669 100755 --- a/testcases/kernel/mem/thp/thp03.c +++ b/testcases/kernel/mem/thp/thp03.c @@ -36,7 +36,8 @@ #include #include #include -#include "mem.h" +#include "tst_test.h" +#include "thp.h" #include "lapi/mmap.h" static void thp_test(void); @@ -83,7 +84,7 @@ static void setup(void) check_hugepage(); - hugepage_size = SAFE_READ_MEMINFO("Hugepagesize:") * KB; + hugepage_size = SAFE_READ_MEMINFO("Hugepagesize:") * TST_KB; unaligned_size = hugepage_size * 4 - 1; page_size = SAFE_SYSCONF(_SC_PAGESIZE); } diff --git a/testcases/kernel/mem/thp/thp04.c b/testcases/kernel/mem/thp/thp04.c index b5f518ac..16d766c3 100755 --- a/testcases/kernel/mem/thp/thp04.c +++ b/testcases/kernel/mem/thp/thp04.c @@ -161,7 +161,7 @@ static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 150, + .runtime = 150, .tags = (const struct tst_tag[]) { {"linux-git", "a8f97366452e"}, {"linux-git", "8310d48b125d"}, diff --git a/testcases/kernel/mem/tunable/Makefile b/testcases/kernel/mem/tunable/Makefile index 80d64bb4..e0014e68 100755 --- a/testcases/kernel/mem/tunable/Makefile +++ b/testcases/kernel/mem/tunable/Makefile @@ -4,5 +4,4 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/mem/tunable/max_map_count.c b/testcases/kernel/mem/tunable/max_map_count.c index f3309547..71a7bbee 100755 --- a/testcases/kernel/mem/tunable/max_map_count.c +++ b/testcases/kernel/mem/tunable/max_map_count.c @@ -48,10 +48,13 @@ #include #include #include -#include "mem.h" +#include "tst_test.h" #define MAP_COUNT_DEFAULT 1024 -#define MAX_MAP_COUNT 65536L +#define MAX_MAP_COUNT_MAX 65536L + +#define OVERCOMMIT_MEMORY "/proc/sys/vm/overcommit_memory" +#define MAX_MAP_COUNT "/proc/sys/vm/max_map_count" /* This is a filter to exclude map entries which aren't accounted * for in the vm_area_struct's map_count. @@ -140,15 +143,15 @@ static void max_map_count_test(void) memfree = SAFE_READ_MEMINFO("CommitLimit:") - SAFE_READ_MEMINFO("Committed_AS:"); /* 64 used as a bias to make sure no overflow happen */ max_iters = memfree / sysconf(_SC_PAGESIZE) * 1024 - 64; - if (max_iters > MAX_MAP_COUNT) - max_iters = MAX_MAP_COUNT; + if (max_iters > MAX_MAP_COUNT_MAX) + max_iters = MAX_MAP_COUNT_MAX; max_maps = MAP_COUNT_DEFAULT; if (max_iters < max_maps) tst_brk(TCONF, "test requires more free memory"); while (max_maps <= max_iters) { - set_sys_tune("max_map_count", max_maps, 1); + TST_SYS_CONF_LONG_SET(MAX_MAP_COUNT, max_maps, 1); switch (pid = SAFE_FORK()) { case 0: @@ -192,8 +195,8 @@ static struct tst_test test = { .forks_child = 1, .test_all = max_map_count_test, .save_restore = (const struct tst_path_val[]) { - {"/proc/sys/vm/overcommit_memory", "0", TST_SR_TBROK}, - {"/proc/sys/vm/max_map_count", NULL, TST_SR_TBROK}, + {OVERCOMMIT_MEMORY, "0", TST_SR_TBROK}, + {MAX_MAP_COUNT, NULL, TST_SR_TBROK}, {} }, }; diff --git a/testcases/kernel/mem/tunable/min_free_kbytes.c b/testcases/kernel/mem/tunable/min_free_kbytes.c index 19da409e..68caf1b0 100755 --- a/testcases/kernel/mem/tunable/min_free_kbytes.c +++ b/testcases/kernel/mem/tunable/min_free_kbytes.c @@ -35,10 +35,14 @@ #include #include #include "lapi/abisize.h" -#include "mem.h" +#include "tst_test.h" #define MAP_SIZE (1UL<<20) +#define OVERCOMMIT_MEMORY "/proc/sys/vm/overcommit_memory" +#define MIN_FREE_KBYTES "/proc/sys/vm/min_free_kbytes" +#define PANIC_ON_OOM "/proc/sys/vm/panic_on_oom" + volatile int end; static long default_tune = -1; static unsigned long total_mem; @@ -88,16 +92,13 @@ static void test_tune(unsigned long overcommit_policy) int ret, i; unsigned long tune, memfree, memtotal; - set_sys_tune("overcommit_memory", overcommit_policy, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_MEMORY, overcommit_policy, 1); for (i = 0; i < 3; i++) { - /* case1 */ if (i == 0) - set_sys_tune("min_free_kbytes", default_tune, 1); - /* case2 */ + TST_SYS_CONF_LONG_SET(MIN_FREE_KBYTES, default_tune, 1); else if (i == 1) { - set_sys_tune("min_free_kbytes", 2 * default_tune, 1); - /* case3 */ + TST_SYS_CONF_LONG_SET(MIN_FREE_KBYTES, 2 * default_tune, 1); } else { memfree = SAFE_READ_MEMINFO("MemFree:"); memtotal = SAFE_READ_MEMINFO("MemTotal:"); @@ -105,7 +106,7 @@ static void test_tune(unsigned long overcommit_policy) if (tune > (memtotal / 50)) tune = memtotal / 50; - set_sys_tune("min_free_kbytes", tune, 1); + TST_SYS_CONF_LONG_SET(MIN_FREE_KBYTES, tune, 1); } fflush(stdout); @@ -189,7 +190,7 @@ static void check_monitor(void) while (end) { memfree = SAFE_READ_MEMINFO("MemFree:"); - tune = get_sys_tune("min_free_kbytes"); + tune = TST_SYS_CONF_LONG_GET(MIN_FREE_KBYTES); if (memfree < tune) { tst_res(TINFO, "MemFree is %lu kB, " @@ -208,25 +209,25 @@ static void sighandler(int signo LTP_ATTRIBUTE_UNUSED) static void setup(void) { - if (get_sys_tune("panic_on_oom")) { + if (TST_SYS_CONF_LONG_GET(PANIC_ON_OOM)) { tst_brk(TCONF, "panic_on_oom is set, disable it to run these testcases"); } total_mem = SAFE_READ_MEMINFO("MemTotal:") + SAFE_READ_MEMINFO("SwapTotal:"); - default_tune = get_sys_tune("min_free_kbytes"); + default_tune = TST_SYS_CONF_LONG_GET(MIN_FREE_KBYTES); } static struct tst_test test = { .needs_root = 1, .forks_child = 1, - .max_runtime = TST_UNLIMITED_RUNTIME, + .timeout = TST_UNLIMITED_TIMEOUT, .setup = setup, .test_all = min_free_kbytes_test, .save_restore = (const struct tst_path_val[]) { - {"/proc/sys/vm/overcommit_memory", NULL, TST_SR_TBROK}, - {"/proc/sys/vm/min_free_kbytes", NULL, TST_SR_TBROK}, + {OVERCOMMIT_MEMORY, NULL, TST_SR_TBROK}, + {MIN_FREE_KBYTES, NULL, TST_SR_TBROK}, {} }, }; diff --git a/testcases/kernel/mem/tunable/overcommit_memory.c b/testcases/kernel/mem/tunable/overcommit_memory.c index ffb7a6d9..9b2cb479 100755 --- a/testcases/kernel/mem/tunable/overcommit_memory.c +++ b/testcases/kernel/mem/tunable/overcommit_memory.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2012-2020 Linux Test Project + * Copyright (c) 2012-2023 Linux Test Project * Copyright (c) 2012-2017 Red Hat, Inc. * * There are two tunables overcommit_memory and overcommit_ratio under @@ -62,13 +62,16 @@ #include #include #include -#include "lapi/abisize.h" -#include "mem.h" +#include "tst_common.h" +#include "tst_test.h" #define DEFAULT_OVER_RATIO 50L #define EXPECT_PASS 0 #define EXPECT_FAIL 1 +#define OVERCOMMIT_MEMORY "/proc/sys/vm/overcommit_memory" +#define OVERCOMMIT_RATIO "/proc/sys/vm/overcommit_ratio" + static char *R_opt; static long old_overcommit_ratio = -1; static long overcommit_ratio; @@ -94,7 +97,7 @@ static void setup(void) else overcommit_ratio = DEFAULT_OVER_RATIO; - old_overcommit_ratio = get_sys_tune("overcommit_ratio"); + old_overcommit_ratio = TST_SYS_CONF_LONG_GET(OVERCOMMIT_RATIO); mem_total = SAFE_READ_MEMINFO("MemTotal:"); tst_res(TINFO, "MemTotal is %ld kB", mem_total); @@ -116,7 +119,7 @@ static void setup(void) SAFE_SETRLIMIT(RLIMIT_AS, &lim); } - set_sys_tune("overcommit_ratio", overcommit_ratio, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_RATIO, overcommit_ratio, 1); calculate_total_batch_size(); tst_res(TINFO, "TotalBatchSize is %ld kB", total_batch_size); @@ -124,12 +127,8 @@ static void setup(void) static void overcommit_memory_test(void) { - -#ifdef TST_ABI32 - tst_brk(TCONF, "test is not designed for 32-bit system."); -#endif /* start to test overcommit_memory=2 */ - set_sys_tune("overcommit_memory", 2, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_MEMORY, 2, 1); update_mem_commit(); alloc_and_check(commit_left * 2, EXPECT_FAIL); @@ -138,14 +137,14 @@ static void overcommit_memory_test(void) alloc_and_check(commit_left / 2, EXPECT_PASS); /* start to test overcommit_memory=0 */ - set_sys_tune("overcommit_memory", 0, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_MEMORY, 0, 1); update_mem(); alloc_and_check(free_total / 2, EXPECT_PASS); alloc_and_check(sum_total * 2, EXPECT_FAIL); /* start to test overcommit_memory=1 */ - set_sys_tune("overcommit_memory", 1, 1); + TST_SYS_CONF_LONG_SET(OVERCOMMIT_MEMORY, 1, 1); alloc_and_check(sum_total / 2, EXPECT_PASS); alloc_and_check(sum_total, EXPECT_PASS); @@ -157,7 +156,7 @@ static int heavy_malloc(long size) { char *p; - p = malloc(size * KB); + LTP_VAR_USED(p) = malloc(size * TST_KB); if (p != NULL) { tst_res(TINFO, "malloc %ld kB successfully", size); free(p); @@ -245,7 +244,7 @@ static void calculate_total_batch_size(void) /* there are ncpu separate counters, that can all grow up to * batch_size. So the maximum error for __vm_enough_memory is * batch_size * ncpus. */ - total_batch_size = (batch_size * ncpus * pagesize) / KB; + total_batch_size = (batch_size * ncpus * pagesize) / TST_KB; } static struct tst_test test = { @@ -256,9 +255,10 @@ static struct tst_test test = { }, .setup = setup, .test_all = overcommit_memory_test, + .skip_in_compat = 1, .save_restore = (const struct tst_path_val[]) { - {"/proc/sys/vm/overcommit_memory", NULL, TST_SR_TBROK}, - {"/proc/sys/vm/overcommit_ratio", NULL, TST_SR_TBROK}, + {OVERCOMMIT_MEMORY, NULL, TST_SR_TBROK}, + {OVERCOMMIT_RATIO, NULL, TST_SR_TBROK}, {} }, }; diff --git a/testcases/kernel/mem/vma/Makefile b/testcases/kernel/mem/vma/Makefile index 057091e1..badec593 100755 --- a/testcases/kernel/mem/vma/Makefile +++ b/testcases/kernel/mem/vma/Makefile @@ -6,7 +6,7 @@ top_srcdir ?= ../../../.. vma05_vdso: CFLAGS+=-ggdb3 include $(top_srcdir)/include/mk/testcases.mk -include $(top_srcdir)/testcases/kernel/mem/include/libmem.mk +include $(top_srcdir)/testcases/kernel/include/lib.mk INSTALL_TARGETS := vma05.sh diff --git a/testcases/kernel/mem/vma/vma05.sh b/testcases/kernel/mem/vma/vma05.sh index c9e4becd..34a82162 100755 --- a/testcases/kernel/mem/vma/vma05.sh +++ b/testcases/kernel/mem/vma/vma05.sh @@ -1,6 +1,10 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2017 Red Hat, Inc. +# Copyright (C) 2024 Cyril Hrubis +# +# --- +# doc # Regression test if the vsyscall and vdso VMA regions are reported correctly. # # While [vsyscall] is mostly deprecated with newer systems, there is @@ -8,39 +12,40 @@ # CONFIG_LEGACY_VSYSCALL_EMULATE (see linux/arch/x86/Kconfig for option # descriptions). First part of the test will check eligible kernels for # regression for a bug fixed by commit 103efcd9aac1 (fix perms/range of -# vsyscall vma in /proc/*/maps). +# vsyscall vma in /proc/\*/maps). # # Second part of test checks [vdso] VMA permissions (fixed with commits # b6558c4a2378 (fix [vdso] page permissions) and e5b97dde514f (Add # VM_ALWAYSDUMP)). As a consequence of this bug, VMAs were not included # in core dumps which resulted in eg. incomplete backtraces and invalid # core dump files created by gdb. +# --- +# +# --- +# env +# { +# "needs_root": true, +# "needs_tmpdir": true, +# "needs_cmds": ["gdb", "uname"], +# "save_restore": [ +# ["/proc/sys/kernel/core_pattern", "core", "TBROK"], +# ["/proc/sys/kernel/core_uses_pid", "0", "TBROK"] +# ], +# "tags": [ +# ["linux-git", "103efcd9aac1"], +# ["linux-git", "b6558c4a2378"], +# ["linux-git", "e5b97dde514f"] +# ] +# } +# --- -TST_SETUP=setup -TST_CLEANUP=cleanup -TST_TESTFUNC=vma_report_check -TST_NEEDS_ROOT=1 -TST_NEEDS_TMPDIR=1 -TST_NEEDS_CMDS="gdb" +. tst_loader.sh -CORE_LIMIT=$(ulimit -c) -CORE_PATTERN=$(cat /proc/sys/kernel/core_pattern) - -setup() +tst_test() { ulimit -c unlimited - echo "core" > /proc/sys/kernel/core_pattern unset DEBUGINFOD_URLS -} -cleanup() -{ - ulimit -c "$CORE_LIMIT" - echo "$CORE_PATTERN" > /proc/sys/kernel/core_pattern -} - -vma_report_check() -{ if [ $(uname -m) = "x86_64" ]; then if LINE=$(grep "vsyscall" /proc/self/maps); then RIGHT="ffffffffff600000-ffffffffff601000[[:space:]][r-]-xp" @@ -54,14 +59,18 @@ vma_report_check() rm -rf core* { vma05_vdso; } > /dev/null 2>&1 + [ -f core ] || tst_brk TBROK "missing core file" + TRACE=$(gdb -silent -ex="thread apply all backtrace" -ex="quit"\ vma05_vdso ./core* 2> /dev/null) - if echo "$TRACE" | grep -qF "??"; then - tst_res TFAIL "[vdso] bug not patched" + + # Only check for ?? symbols in application code, not system libraries + APP_UNKNOWN=$(echo "$TRACE" | grep -F "??" | grep -v -e "from /lib/" -e "from /usr/lib/") + if [ -n "$APP_UNKNOWN" ]; then + tst_res TFAIL "[vdso] bug not patched - unknown symbols in application code" else tst_res TPASS "[vdso] backtrace complete" fi } -. tst_test.sh -tst_run +. tst_run.sh diff --git a/testcases/kernel/pty/.gitignore b/testcases/kernel/pty/.gitignore index acca3db9..2d0c8bb6 100755 --- a/testcases/kernel/pty/.gitignore +++ b/testcases/kernel/pty/.gitignore @@ -1,5 +1,10 @@ /hangup01 /ptem01 +/ptem02 +/ptem03 +/ptem04 +/ptem05 +/ptem06 /pty01 /pty02 /pty03 @@ -7,3 +12,5 @@ /pty05 /pty06 /pty07 +/pty08 +/pty09 diff --git a/testcases/kernel/pty/common.h b/testcases/kernel/pty/common.h new file mode 100644 index 00000000..7cda1609 --- /dev/null +++ b/testcases/kernel/pty/common.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef PTEM_H +#define PTEM_H + +#define _GNU_SOURCE + +#include "tst_test.h" + +#define MASTERCLONE "/dev/ptmx" + +static inline int open_master(void) +{ + int masterfd; + + if (access(MASTERCLONE, F_OK)) + tst_brk(TCONF, "%s device doesn't exist", MASTERCLONE); + + tst_res(TINFO, "opening master %s", MASTERCLONE); + + masterfd = SAFE_OPEN(MASTERCLONE, O_RDWR); + + if (grantpt(masterfd) == -1) + tst_brk(TBROK | TERRNO, "grantpt() error"); + + if (unlockpt(masterfd) == -1) + tst_brk(TBROK | TERRNO, "unlockpt() error"); + + return masterfd; +} + +static inline int open_slave(const int masterfd) +{ + int slavefd; + char *slavename; + + slavename = SAFE_PTSNAME(masterfd); + + tst_res(TINFO, "opening slave %s", slavename); + +#ifndef __BIONIC__ + /* grantpt() is a no-op in bionic. */ + struct stat st; + + SAFE_STAT(slavename, &st); + + uid_t uid = getuid(); + + if (st.st_uid != uid) { + tst_brk(TBROK, "uid mismatch st.st_uid(%d) != getuid(%d)", + st.st_uid, uid); + } + + if (st.st_mode != (S_IFCHR | 0620) && + st.st_mode != (S_IFCHR | 0600)) { + tst_brk(TBROK, "unexpected slave device permission: %o", + st.st_mode); + } +#endif + + slavefd = SAFE_OPEN(slavename, O_RDWR); + + return slavefd; +} + +#endif diff --git a/testcases/kernel/pty/ptem01.c b/testcases/kernel/pty/ptem01.c index 20ef5d0a..3c364b24 100755 --- a/testcases/kernel/pty/ptem01.c +++ b/testcases/kernel/pty/ptem01.c @@ -1,441 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2002 - * Copyright (c) 2020 Petr Vorel - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2020 Petr Vorel + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -/* 12/23/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ +/*\ + * Verify that it's possible to open a pseudo-terminal via /dev/ptmx, obtain a + * slave device and configure termos/termios ioctls. + */ #define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include +#include "common.h" #include "lapi/ioctl.h" -char *TCID = "ptem01"; /* Test program identifier. */ -int TST_TOTAL = 6; /* Total number of test cases. */ -/**************/ +static int masterfd = -1; -/* - * pty master clone device - */ -#define MASTERCLONE "/dev/ptmx" - -#define BUFSZ 4096 - -/* - * test termio/termios ioctls - */ -int test1(void) +static void run(void) { - int masterfd, slavefd; - char *slavename; + int slavefd; struct termio termio; struct termios termios; - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); + slavefd = open_slave(masterfd); - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); - } + TST_EXP_PASS(ioctl(slavefd, TCGETS, &termios)); + TST_EXP_PASS(ioctl(slavefd, TCSETS, &termios)); + TST_EXP_PASS(ioctl(slavefd, TCSETSW, &termios)); + TST_EXP_PASS(ioctl(slavefd, TCSETSF, &termios)); + TST_EXP_PASS(ioctl(slavefd, TCSETS, &termios)); + TST_EXP_PASS(ioctl(slavefd, TCGETA, &termio)); + TST_EXP_PASS(ioctl(slavefd, TCSETA, &termio)); + TST_EXP_PASS(ioctl(slavefd, TCSETAW, &termio)); + TST_EXP_PASS(ioctl(slavefd, TCSETAF, &termio)); - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK, NULL, "unlockpt() call failed"); - } - - if ((slavefd = open(slavename, O_RDWR)) < 0) { - tst_brkm(TFAIL, NULL, "Could not open %s", slavename); - } - - if (ioctl(slavefd, TCGETS, &termios) != 0) { - tst_brkm(TFAIL, NULL, "TCGETS"); - } - - if (ioctl(slavefd, TCSETS, &termios) != 0) { - tst_brkm(TFAIL, NULL, "TCSETS"); - } - - if (ioctl(slavefd, TCSETSW, &termios) != 0) { - tst_brkm(TFAIL, NULL, "TCSETSW"); - } - - if (ioctl(slavefd, TCSETSF, &termios) != 0) { - tst_brkm(TFAIL, NULL, "TCSETSF"); - } - - if (ioctl(slavefd, TCSETS, &termios) != 0) { - tst_brkm(TFAIL, NULL, "TCSETS"); - } - - if (ioctl(slavefd, TCGETA, &termio) != 0) { - tst_brkm(TFAIL, NULL, "TCGETA"); - } - - if (ioctl(slavefd, TCSETA, &termio) != 0) { - tst_brkm(TFAIL, NULL, "TCSETA"); - } - - if (ioctl(slavefd, TCSETAW, &termio) != 0) { - tst_brkm(TFAIL, NULL, "TCSETAW"); - } - - if (ioctl(slavefd, TCSETAF, &termio) != 0) { - tst_brkm(TFAIL, NULL, "TCSETAF"); - } - - if (close(slavefd) != 0) { - tst_brkm(TBROK, NULL, "close slave"); - } - - if (close(masterfd) != 0) { - tst_brkm(TBROK, NULL, "close master"); - } - tst_resm(TPASS, "test1"); - - /** NOT REACHED **/ - return 0; + SAFE_CLOSE(slavefd); } -/* - * test window size setting and getting - */ -int test2(void) +static void setup(void) { - int masterfd, slavefd; - char *slavename; - struct winsize wsz; - struct winsize wsz1 = { 24, 80, 5, 10 }; - struct winsize wsz2 = { 60, 100, 11, 777 }; - - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); - - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); - } - - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK, NULL, "unlockpt() call failed"); - } - - if ((slavefd = open(slavename, O_RDWR)) < 0) { - tst_brkm(TBROK, NULL, "Could not open %s", slavename); - } - - if (ioctl(masterfd, TIOCSWINSZ, &wsz1) != 0) { - tst_brkm(TFAIL, NULL, "TIOCSWINSZ"); - } - - if (ioctl(slavefd, TIOCGWINSZ, &wsz) != 0) { - tst_brkm(TFAIL, NULL, "TIOCGWINSZ"); - } - - if (wsz.ws_row != wsz1.ws_row || wsz.ws_col != wsz1.ws_col || - wsz.ws_xpixel != wsz1.ws_xpixel || - wsz.ws_ypixel != wsz1.ws_ypixel) { - tst_brkm(TFAIL, NULL, "unexpected window size returned"); - } - - if (ioctl(masterfd, TIOCGWINSZ, &wsz) != 0) { - tst_brkm(TFAIL, NULL, "TIOCGWINSZ"); - } - - if (wsz.ws_row != wsz1.ws_row || wsz.ws_col != wsz1.ws_col || - wsz.ws_xpixel != wsz1.ws_xpixel || - wsz.ws_ypixel != wsz1.ws_ypixel) { - tst_brkm(TFAIL, NULL, "unexpected window size returned"); - } - - if (ioctl(slavefd, TIOCSWINSZ, &wsz2) != 0) { - tst_brkm(TFAIL, NULL, "TIOCSWINSZ"); - } - - if (ioctl(slavefd, TIOCGWINSZ, &wsz) != 0) { - tst_brkm(TFAIL, NULL, "TIOCGWINSZ"); - } - - if (wsz.ws_row != wsz2.ws_row || wsz.ws_col != wsz2.ws_col || - wsz.ws_xpixel != wsz2.ws_xpixel || - wsz.ws_ypixel != wsz2.ws_ypixel) { - tst_brkm(TFAIL, NULL, "unexpected window size returned"); - } - - if (close(slavefd) != 0) { - tst_brkm(TBROK, NULL, "close"); - } - - if (close(masterfd) != 0) { - tst_brkm(TBROK, NULL, "close"); - } - tst_resm(TPASS, "test2"); - - /** NOT REACHED **/ - return 0; + masterfd = open_master(); } -/* - * test sending a break - */ -int test3(void) +static void cleanup(void) { - int masterfd, slavefd; - char *slavename; - - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); - - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); - } - - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK, NULL, "unlockpt() call failed"); - } - - if ((slavefd = open(slavename, O_RDWR)) < 0) { - tst_brkm(TBROK, NULL, "Could not open %s", slavename); - } - - if (tcsendbreak(masterfd, 10) != 0) { - tst_brkm(TFAIL, NULL, "tcsendbreak"); - } - - if (tcsendbreak(slavefd, 10) != 0) { - tst_brkm(TFAIL, NULL, "tcsendbreak"); - } - - if (close(slavefd) != 0) { - tst_brkm(TBROK, NULL, "close slave"); - } - - if (close(masterfd) != 0) { - tst_brkm(TBROK, NULL, "close master"); - } - tst_resm(TPASS, "test3"); - - /** NOT REACHED **/ - return 0; + if (masterfd != -1) + SAFE_CLOSE(masterfd); } -/* - * test multiple opens of slave side - */ -int test4(void) -{ - int masterfd, slavefd, slavefd2, slavefd3; - char *slavename; - - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); - - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); - } - - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK, NULL, "unlockpt() call failed"); - } - - if ((slavefd = open(slavename, O_RDWR)) < 0) { - tst_brkm(TBROK, NULL, "Could not open %s", slavename); - } - - if ((slavefd2 = open(slavename, O_RDWR)) < 0) { - tst_brkm(TFAIL, NULL, "Could not open %s (again)", slavename); - } - - if ((slavefd3 = open(slavename, O_RDWR)) < 0) { - tst_brkm(TFAIL, NULL, "Could not open %s (once more)", - slavename); - } - - if (close(slavefd) != 0) { - tst_brkm(TBROK, NULL, "close slave"); - } - if (close(slavefd2) != 0) { - tst_brkm(TBROK, NULL, "close slave again"); - } - if (close(slavefd3) != 0) { - tst_brkm(TBROK, NULL, "close slave once more"); - } - if (close(masterfd) != 0) { - tst_brkm(TBROK, NULL, "close master"); - } - tst_resm(TPASS, "test4"); - - /** NOT REACHED **/ - return 0; -} - -#define NUMOPENS 6 - -/* - * test several simultaneous opens - */ -int test5(void) -{ - static int masterfd[NUMOPENS]; - static int slavefd[NUMOPENS]; - char *slavename; - int i; - - for (i = 0; i < NUMOPENS; ++i) { - masterfd[i] = open(MASTERCLONE, O_RDWR); - if (masterfd[i] < 0) { - tst_resm(TBROK, "%s", MASTERCLONE); - tst_resm(TBROK, "out of ptys"); - for (i = 0; i < NUMOPENS; ++i) { - if (masterfd[i] != 0) { - (void)close(masterfd[i]); - } - if (slavefd[i] != 0) { - (void)close(slavefd[i]); - } - } - tst_exit(); - } - - slavename = ptsname(masterfd[i]); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, - "ptsname() call failed"); - } - - if (grantpt(masterfd[i]) != 0) { - tst_brkm(TBROK | TERRNO, NULL, - "grantpt() call failed"); - } - - if (unlockpt(masterfd[i]) != 0) { - tst_brkm(TBROK, NULL, "unlockpt() call failed"); - } - - if ((slavefd[i] = open(slavename, O_RDWR)) < 0) { - tst_brkm(TFAIL, NULL, - "Iteration %d: Could not open %s", i, - slavename); - } - - } - - for (i = 0; i < NUMOPENS; ++i) { - if (close(slavefd[i]) != 0) { - tst_brkm(TBROK, NULL, "Iteration %d: close slave", i); - } - if (close(masterfd[i]) != 0) { - tst_brkm(TBROK, NULL, "close master"); - } - } - tst_resm(TPASS, "test5"); - - /** NOT REACHED **/ - return 0; -} - -/* - * test hangup semantics - */ -int test6(void) -{ - static int masterfd; - static int slavefd; - char *slavename; - struct termios termios; - - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); - - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); - } - - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK, NULL, "unlockpt() call failed"); - } - - if ((slavefd = open(slavename, O_RDWR)) < 0) { - tst_brkm(TBROK, NULL, "Could not open %s", slavename); - } - - if (ioctl(slavefd, TCGETS, &termios) != 0) { - tst_brkm(TFAIL, NULL, "TCGETS"); - } - - termios.c_cflag &= ~CBAUD; - termios.c_cflag |= B0 & CBAUD; - if (ioctl(slavefd, TCSETS, &termios) != 0) { - tst_brkm(TFAIL, NULL, "TCGETS"); - } - - if (close(slavefd) != 0) { - tst_brkm(TBROK, NULL, "close"); - } - if (close(masterfd) != 0) { - tst_brkm(TBROK, NULL, "close"); - } - tst_resm(TPASS, "test6"); - - /** NOT REACHED **/ - return 0; -} - -/* - * main test driver - */ -int main(void) -{ - test1(); - test2(); - test3(); - test4(); - test5(); - test6(); - /* - * all done - */ - tst_exit(); -} +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/pty/ptem02.c b/testcases/kernel/pty/ptem02.c new file mode 100644 index 00000000..ee02d20e --- /dev/null +++ b/testcases/kernel/pty/ptem02.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2020 Petr Vorel + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that it's possible to open a pseudo-terminal via /dev/ptmx, obtain a + * slave device and set/get window size. + */ + +#define _GNU_SOURCE + +#include "common.h" + +static int masterfd = -1; + +static void run(void) +{ + int slavefd; + struct winsize wsz; + struct winsize wsz1 = { 24, 80, 5, 10 }; + struct winsize wsz2 = { 60, 100, 11, 777 }; + + slavefd = open_slave(masterfd); + + TST_EXP_PASS(ioctl(masterfd, TIOCSWINSZ, &wsz1)); + TST_EXP_PASS(ioctl(slavefd, TIOCGWINSZ, &wsz)); + + TST_EXP_EQ_LI(wsz.ws_row, wsz1.ws_row); + TST_EXP_EQ_LI(wsz.ws_col, wsz1.ws_col); + TST_EXP_EQ_LI(wsz.ws_xpixel, wsz1.ws_xpixel); + TST_EXP_EQ_LI(wsz.ws_ypixel, wsz1.ws_ypixel); + + TST_EXP_PASS(ioctl(masterfd, TIOCGWINSZ, &wsz)); + + TST_EXP_EQ_LI(wsz.ws_row, wsz1.ws_row); + TST_EXP_EQ_LI(wsz.ws_col, wsz1.ws_col); + TST_EXP_EQ_LI(wsz.ws_xpixel, wsz1.ws_xpixel); + TST_EXP_EQ_LI(wsz.ws_ypixel, wsz1.ws_ypixel); + + TST_EXP_PASS(ioctl(slavefd, TIOCSWINSZ, &wsz2)); + TST_EXP_PASS(ioctl(slavefd, TIOCGWINSZ, &wsz)); + + TST_EXP_EQ_LI(wsz.ws_row, wsz2.ws_row); + TST_EXP_EQ_LI(wsz.ws_col, wsz2.ws_col); + TST_EXP_EQ_LI(wsz.ws_xpixel, wsz2.ws_xpixel); + TST_EXP_EQ_LI(wsz.ws_ypixel, wsz2.ws_ypixel); + + SAFE_CLOSE(slavefd); +} + +static void setup(void) +{ + masterfd = open_master(); +} + +static void cleanup(void) +{ + if (masterfd != -1) + SAFE_CLOSE(masterfd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/pty/ptem03.c b/testcases/kernel/pty/ptem03.c new file mode 100644 index 00000000..d284371f --- /dev/null +++ b/testcases/kernel/pty/ptem03.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2020 Petr Vorel + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that it's possible to open a pseudo-terminal via /dev/ptmx, obtain a + * slave device and to send a break to both master and slave. + */ + +#define _GNU_SOURCE + +#include +#include "common.h" + +static int masterfd = -1; + +static void run(void) +{ + int slavefd; + + slavefd = open_slave(masterfd); + + TST_EXP_PASS(tcsendbreak(masterfd, 10)); + TST_EXP_PASS(tcsendbreak(slavefd, 10)); + + SAFE_CLOSE(slavefd); +} + +static void setup(void) +{ + masterfd = open_master(); +} + +static void cleanup(void) +{ + if (masterfd != -1) + SAFE_CLOSE(masterfd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/pty/ptem04.c b/testcases/kernel/pty/ptem04.c new file mode 100644 index 00000000..14249448 --- /dev/null +++ b/testcases/kernel/pty/ptem04.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2020 Petr Vorel + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that it's possible to open a pseudo-terminal via /dev/ptmx, obtain a + * slave device and to check if it's possible to open it multiple times. + */ + +#define _GNU_SOURCE + +#include "common.h" + +#define NUM_SLAVES 10 + +static int masterfd = -1; + +static void run(void) +{ + int slavefd[NUM_SLAVES]; + + for (int i = 0; i < NUM_SLAVES; i++) + slavefd[i] = TST_EXP_FD(open_slave(masterfd)); + + for (int i = 0; i < NUM_SLAVES; i++) + SAFE_CLOSE(slavefd[i]); +} + +static void setup(void) +{ + masterfd = open_master(); +} + +static void cleanup(void) +{ + if (masterfd != -1) + SAFE_CLOSE(masterfd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/pty/ptem05.c b/testcases/kernel/pty/ptem05.c new file mode 100644 index 00000000..7c4377c3 --- /dev/null +++ b/testcases/kernel/pty/ptem05.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2020 Petr Vorel + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that it's possible to open a pseudo-terminal via /dev/ptmx, to obtain + * a master + slave pair and to open them multiple times. + */ + +#define _GNU_SOURCE + +#include "common.h" + +#define NUM_OPENS 10 + +static int masterfd[NUM_OPENS]; + +static void run(void) +{ + int slavefd[NUM_OPENS]; + + for (int i = 0; i < NUM_OPENS; i++) + slavefd[i] = TST_EXP_FD(open_slave(masterfd[i])); + + for (int i = 0; i < NUM_OPENS; i++) + SAFE_CLOSE(slavefd[i]); +} + +static void setup(void) +{ + for (int i = 0; i < NUM_OPENS; i++) + masterfd[i] = -1; + + for (int i = 0; i < NUM_OPENS; i++) + masterfd[i] = open_master(); +} + +static void cleanup(void) +{ + for (int i = 0; i < NUM_OPENS; i++) { + if (masterfd[i] != -1) + SAFE_CLOSE(masterfd[i]); + } +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/pty/ptem06.c b/testcases/kernel/pty/ptem06.c new file mode 100644 index 00000000..6845b88f --- /dev/null +++ b/testcases/kernel/pty/ptem06.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2020 Petr Vorel + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that it's possible to open a pseudo-terminal via /dev/ptmx, to obtain + * a slave device and to set baudrate to B0 (which means hang up). + */ + +#define _GNU_SOURCE + +#include +#include "common.h" + +static int masterfd = -1; + +static void run(void) +{ + int slavefd; + struct termios termios; + + slavefd = open_slave(masterfd); + + TST_EXP_PASS(ioctl(slavefd, TCGETS, &termios)); + termios.c_cflag &= ~CBAUD; + termios.c_cflag |= B0 & CBAUD; + TST_EXP_PASS(ioctl(slavefd, TCSETS, &termios)); + + SAFE_CLOSE(slavefd); +} + +static void setup(void) +{ + masterfd = open_master(); +} + +static void cleanup(void) +{ + if (masterfd != -1) + SAFE_CLOSE(masterfd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/pty/pty01.c b/testcases/kernel/pty/pty01.c index 666b221b..99f8d32d 100755 --- a/testcases/kernel/pty/pty01.c +++ b/testcases/kernel/pty/pty01.c @@ -1,395 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -/* 12/23/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ +/*\ + * Verify that write/read is properly working when master and slave + * pseudo terminals communicate with each other. + */ #define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" -#include "lapi/ioctl.h" +#include "common.h" -char *TCID = "pty01"; /* Test program identifier. */ -int TST_TOTAL = 5; /* Total number of test cases. */ -/**************/ - -/* - * pty master clone device - */ -#define MASTERCLONE "/dev/ptmx" - -/* - * string for testing read/write on ptys - */ #define STRING "Linux Test Project\n" -/* - * test buffer size - */ -#define TESTSIZE 1024 +static size_t string_len; +static int masterfd = -1; +static int slavefd = -1; -/* - * mode we expect grantpt() to leave pty as - */ -#define PTY_MODE 020622 - -/* - * number of procs for parallel test - */ -#define NUMPROCS 15 - -/* - * test slave locking - */ -static int test1(void) +static void run(void) { - int masterfd; /* master pty fd */ - int slavefd; /* slave pty fd */ - char *slavename; - struct stat st; - char buf[TESTSIZE]; + char buf[BUFSIZ]; - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); + tst_res(TINFO, "Send message to master and read it from slave"); - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); - } + memset(buf, 0, BUFSIZ); + SAFE_WRITE(SAFE_WRITE_ALL, masterfd, STRING, string_len); + SAFE_READ(0, slavefd, buf, string_len); + TST_EXP_EQ_STRN(STRING, buf, string_len - 1); - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); - } + tst_res(TINFO, "Send message to slave and read it from master"); - if (stat(slavename, &st) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "stat(%s) failed", slavename); - } - if (st.st_uid != getuid()) { - tst_brkm(TBROK, NULL, "uid mismatch"); - } + memset(buf, 0, BUFSIZ); + SAFE_WRITE(SAFE_WRITE_ALL, slavefd, STRING, string_len); - /* grantpt() is a no-op in bionic. */ -#ifndef __BIONIC__ - if (st.st_mode != (S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP)) { - tst_brkm(TBROK, NULL, "mode mismatch (mode=%o)", st.st_mode); - } -#endif - - slavefd = open(slavename, O_RDWR); - if (slavefd >= 0) { - tst_brkm(TBROK, NULL, "open didn't fail as expected!"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "unlockpt() failed"); - } - - slavefd = SAFE_OPEN(NULL, slavename, O_RDWR); - - /* - * test writing to the master / reading from the slave + /* we need to write string_len + 1, because kernel converts newline + * into carriage return + newline */ - if (write(masterfd, STRING, strlen(STRING)) != strlen(STRING)) { - /* - * XXX: the errno printout might be garbage, but better to be - * safe than sorry.. - */ - tst_brkm(TFAIL | TERRNO, NULL, "write to master"); - } - - if (read(slavefd, buf, strlen(STRING)) != strlen(STRING)) { - /* XXX: Same as write above.. */ - tst_brkm(TFAIL | TERRNO, NULL, "read from slave"); - } - if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) { - tst_brkm(TFAIL, NULL, - "strings are different (STRING = '%s' != buf = '%s')", - STRING, buf); - } - - /* - * test writing to the slave / reading from the master - */ - if (write(slavefd, STRING, strlen(STRING)) != strlen(STRING)) { - /* XXX: Same as write above.. */ - tst_brkm(TFAIL | TERRNO, NULL, "write to slave"); - } - - if (read(masterfd, buf, strlen(STRING)) != strlen(STRING)) { - /* XXX: Same as write above.. */ - tst_brkm(TFAIL | TERRNO, NULL, "read from master"); - } - if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) { - tst_brkm(TFAIL, NULL, - "strings are different (STRING = '%s' != buf = '%s').", - STRING, buf); - } - - /* - * try an invalid ioctl on the slave... - */ - if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) { - tst_brkm(TFAIL, NULL, - "invalid slave TIOCGWINSZ ioctl succeeded.. it should " - "have failed"); - } - - /* - * try an invalid ioctl on the master... - */ - if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) { - tst_brkm(TFAIL, NULL, - "invalid master TIOCGWINSZ ioctl succeeded.. it should " - "have failed"); - } - - /* - * close pty fds - */ - if (close(slavefd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "close of slave"); - } - if (close(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "close of master"); - } - tst_resm(TPASS, "test1"); - /** NOTREACHED **/ - return 0; + SAFE_READ(0, masterfd, buf, string_len + 1); + TST_EXP_EQ_STRN(STRING, buf, string_len - 1); } -/* - * test slave operations with closed master - */ -static void test2(void) +static void setup(void) { - int masterfd; /* master pty fd */ - int slavefd; /* slave pty fd */ - int i; - char *slavename; - char c; + masterfd = open_master(); + slavefd = open_slave(masterfd); - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); - - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); - } - - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed"); - } - - slavefd = SAFE_OPEN(NULL, slavename, O_RDWR); - - /* - * close pty fds. See what happens when we close the master - * first. - */ - if (close(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "close()"); - } - - errno = 0; - if ((i = read(slavefd, &c, 1)) == 1) { - tst_brkm(TFAIL, NULL, - "reading from slave fd should have failed, but didn't" - "(read '%c')", c); - } - - if ((i = write(slavefd, &c, 1)) == 1) { - tst_brkm(TFAIL, NULL, - "writing to slave fd should have failed, but didn't"); - } - - if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) { - tst_brkm(TFAIL, NULL, - "trying TIOCGWINSZ on slave fd should have failed, " - "but didn't"); - } - - if (close(slavefd) != 0) { - tst_brkm(TBROK, NULL, "close"); - } - tst_resm(TPASS, "test2"); + string_len = strlen(STRING); } -/* - * test operations on master with closed slave - */ -static void test3(void) +static void cleanup(void) { - int masterfd; /* master pty fd */ + if (masterfd != -1) + SAFE_CLOSE(masterfd); - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); - - if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) { - tst_brkm(TFAIL | TERRNO, NULL, - "trying TIOCGWINSZ on master with no open slave " - "succeeded unexpectedly"); - } - tst_resm(TPASS, "test3"); + if (slavefd != -1) + SAFE_CLOSE(slavefd); } -/* - * test multiple opens on slave side of pty - */ -static void test4(void) -{ - int masterfd; /* master pty fd */ - int slavefd; /* slave pty fd */ - int slavefd2; - int slavefd3; - char *slavename; - - masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); - - slavename = ptsname(masterfd); - if (slavename == NULL) { - tst_brkm(TBROK, NULL, "ptsname() call failed"); - } - - if (grantpt(masterfd) != 0) { - tst_brkm(TBROK, NULL, "grantpt() call failed"); - } - - if (unlockpt(masterfd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed"); - } - - slavefd = SAFE_OPEN(NULL, slavename, O_RDWR); - - slavefd2 = open(slavename, O_RDWR); - if (slavefd < 0) { - tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (again)", - slavename); - } - - slavefd3 = open(slavename, O_RDWR); - if (slavefd < 0) { - tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (once more)", - slavename); - } - - /* - * close pty fds. - */ - if (close(slavefd) != 0) { - tst_brkm(TBROK | TERRNO, NULL, "close slave"); - } - - if (close(slavefd2) != 0) { - tst_brkm(TBROK, NULL, "close slave again"); - } - - if (close(slavefd3) != 0) { - tst_brkm(TBROK, NULL, "close slave once more"); - } - - if (close(masterfd) != 0) { - tst_brkm(TBROK, NULL, "close master"); - } - tst_resm(TPASS, "test4"); -} - -/* - * test opening/closing lots of ptys in parallel. We may run out - * of ptys for this test depending on how the system is configured, - * but that's not a fatal error. - */ -static void test5(void) -{ - int masterfd; /* master pty fd */ - char *slavename; - int status; - int i; - - for (i = 0; i < NUMPROCS; ++i) { - switch (fork()) { - case -1: - tst_brkm(TBROK, NULL, "fork()"); - break; - case 0: - masterfd = open(MASTERCLONE, O_RDWR); - if (masterfd < 0) { - printf("proc %d: opening %s failed: %s", - i, MASTERCLONE, strerror(errno)); - exit(1); - } - if (grantpt(masterfd) != 0) { - printf("proc %d: grantpt() call failed: %s", - i, strerror(errno)); - exit(1); - } - slavename = ptsname(masterfd); - if (slavename == NULL) { - printf("proc %d: ptsname() call failed: %s", - i, strerror(errno)); - exit(1); - } - sleep(10); - if (close(masterfd) != 0) { - printf("proc %d: close failed: %s", - i, strerror(errno)); - exit(1); - } - exit(0); - default: - break; - } - } - while (wait(&status) > 0) { - if (status) { - tst_brkm(TFAIL, NULL, - "child exited with non-zero status %d", - status); - } - } - tst_resm(TPASS, "test5"); -} - -/* - * main test driver - */ -int main(void) -{ - test1(); - test2(); - test3(); - test4(); - test5(); - - /* - * all done - */ - tst_exit(); -} +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/pty/pty03.c b/testcases/kernel/pty/pty03.c index 6324bc87..99c65926 100755 --- a/testcases/kernel/pty/pty03.c +++ b/testcases/kernel/pty/pty03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test based on Syzkaller reproducer: * https://syzkaller.appspot.com/bug?id=680c24ff647dd7241998e19da52e8136d2fd3523 * @@ -152,7 +150,8 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .needs_root = 1, - .max_runtime = 30, + .runtime = 30, + .min_runtime = 16, .needs_kconfigs = (const char *const[]){ "CONFIG_SERIO_SERPORT", NULL diff --git a/testcases/kernel/pty/pty04.c b/testcases/kernel/pty/pty04.c index 8c7b1bf9..20470325 100755 --- a/testcases/kernel/pty/pty04.c +++ b/testcases/kernel/pty/pty04.c @@ -93,6 +93,19 @@ static struct ldisc_info ldiscs[] = { static int ptmx = -1, pts = -1, sk = -1, mtu, no_check; +static void setup(void) +{ + int fd = SAFE_OPEN("/dev/ptmx", O_RDWR); + + TEST(ioctl(fd, TIOCVHANGUP)); + SAFE_CLOSE(fd); + + if (TST_RET && TST_ERR == ENOTTY) + tst_brk(TCONF | TTERRNO, "ioctl(TIOCVHANGUP) not supported"); + else if (TST_RET) + tst_brk(TBROK | TTERRNO, "ioctl(TIOCVHANGUP) failed"); +} + static int set_ldisc(int tty, const struct ldisc_info *ldisc) { TEST(ioctl(tty, TIOCSETD, &ldisc->n)); @@ -467,6 +480,7 @@ static void cleanup(void) } static struct tst_test test = { + .setup = setup, .test = do_test, .cleanup = cleanup, .tcnt = 2, diff --git a/testcases/kernel/pty/pty05.c b/testcases/kernel/pty/pty05.c index 925ce385..fb76d43b 100755 --- a/testcases/kernel/pty/pty05.c +++ b/testcases/kernel/pty/pty05.c @@ -97,7 +97,8 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .taint_check = TST_TAINT_W | TST_TAINT_D, - .max_runtime = 150, + .runtime = 150, + .min_runtime = 16, .tags = (const struct tst_tag[]) { {"linux-git", "82f2341c94d27"}, {"CVE", "2017-2636"}, diff --git a/testcases/kernel/pty/pty06.c b/testcases/kernel/pty/pty06.c index cc95eb1a..00cc6a98 100644 --- a/testcases/kernel/pty/pty06.c +++ b/testcases/kernel/pty/pty06.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test based on Syzkaller reproducer: * https://syzkaller.appspot.com/bug?extid=522643ab5729b0421998 * @@ -97,7 +95,7 @@ static struct tst_test test = { .cleanup = cleanup, .needs_root = 1, .taint_check = TST_TAINT_W | TST_TAINT_D, - .max_runtime = 150, + .runtime = 150, .tags = (const struct tst_tag[]) { {"CVE", "2020-36557"}, {"linux-git", "ca4463bf8438"}, diff --git a/testcases/kernel/pty/pty07.c b/testcases/kernel/pty/pty07.c index cc3df55c..5d1e26a6 100644 --- a/testcases/kernel/pty/pty07.c +++ b/testcases/kernel/pty/pty07.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * The VT_DISALLOCATE ioctl can free a virtual console while VT_RESIZEX ioctl is * still running, causing a use-after-free in vt_ioctl(). Because VT_RESIZEX ioctl * have not make sure vc_cons[i].d is not NULL after grabbing console_lock(). @@ -114,7 +112,8 @@ static struct tst_test test = { .cleanup = cleanup, .needs_root = 1, .taint_check = TST_TAINT_W | TST_TAINT_D, - .max_runtime = 150, + .runtime = 150, + .min_runtime = 16, .tags = (const struct tst_tag[]) { { "linux-git", "6cd1ed50efd8"}, {} diff --git a/testcases/kernel/pty/pty08.c b/testcases/kernel/pty/pty08.c new file mode 100644 index 00000000..2b8f7152 --- /dev/null +++ b/testcases/kernel/pty/pty08.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that slave pseudo-terminal fails reading/writing if master has been + * closed. + */ + +#define _GNU_SOURCE + +#include "common.h" + +static void run(void) +{ + int slavefd; + int masterfd; + char buf; + + masterfd = open_master(); + slavefd = open_slave(masterfd); + + tst_res(TINFO, "Closing master communication"); + SAFE_CLOSE(masterfd); + + TST_EXP_EQ_LI(read(slavefd, &buf, 1), 0); + TST_EXP_FAIL(write(slavefd, &buf, 1), EIO); + + SAFE_CLOSE(slavefd); +} + +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/pty/pty09.c b/testcases/kernel/pty/pty09.c new file mode 100644 index 00000000..f3ade238 --- /dev/null +++ b/testcases/kernel/pty/pty09.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that slave pseudo-terminal can be opened multiple times in parallel. + */ + +#define _GNU_SOURCE + +#include "common.h" + +static int masterfd = -1; + +static unsigned int count_avail_pid(void) +{ + DIR *dir; + struct dirent *ent; + struct rlimit limit; + unsigned int count = 0; + unsigned int max_pid_num; + + SAFE_GETRLIMIT(RLIMIT_NOFILE, &limit); + + dir = SAFE_OPENDIR("/proc/self/fd"); + while ((ent = SAFE_READDIR(dir))) + count++; + + SAFE_CLOSEDIR(dir); + + max_pid_num = limit.rlim_cur - count; + + tst_res(TINFO, "Available number of pids: %u", max_pid_num); + + return max_pid_num; +} + +static void run(void) +{ + unsigned int max_pid_num; + + max_pid_num = count_avail_pid(); + + int slavefd[max_pid_num]; + + for (uint32_t i = 0; i < max_pid_num; i++) + slavefd[i] = open_slave(masterfd); + + tst_res(TPASS, "pty has been opened %d times", max_pid_num); + + for (uint32_t i = 0; i < max_pid_num; i++) + SAFE_CLOSE(slavefd[i]); +} + +static void setup(void) +{ + masterfd = open_master(); +} + +static void cleanup(void) +{ + if (masterfd != -1) + SAFE_CLOSE(masterfd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/sched/cfs-scheduler/cfs_bandwidth01.c b/testcases/kernel/sched/cfs-scheduler/cfs_bandwidth01.c index 27fc0fc5..e52858f8 100755 --- a/testcases/kernel/sched/cfs-scheduler/cfs_bandwidth01.c +++ b/testcases/kernel/sched/cfs-scheduler/cfs_bandwidth01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Creates a multi-level CGroup hierarchy with the cpu controller * enabled. The leaf groups are populated with "busy" processes which * simulate intermittent cpu load. They spin for some time then sleep @@ -174,7 +172,7 @@ static struct tst_test test = { .cleanup = cleanup, .forks_child = 1, .needs_checkpoints = 1, - .max_runtime = 20, + .timeout = 20, .taint_check = TST_TAINT_W | TST_TAINT_D, .needs_kconfigs = (const char *[]) { "CONFIG_CFS_BANDWIDTH", diff --git a/testcases/kernel/sched/cfs-scheduler/starvation.c b/testcases/kernel/sched/cfs-scheduler/starvation.c index eb9fd6ff..045254ba 100644 --- a/testcases/kernel/sched/cfs-scheduler/starvation.c +++ b/testcases/kernel/sched/cfs-scheduler/starvation.c @@ -2,8 +2,6 @@ /* Copyright 2023 Mike Galbraith */ /* Copyright 2023 Wei Gao */ /*\ - * - * [Description] * * Thread starvation test. On fauluty kernel the test timeouts. * @@ -21,11 +19,39 @@ #include #include "tst_test.h" +#include "tst_kconfig.h" +#include "tst_safe_clocks.h" +#include "tst_timer.h" static char *str_loop; -static long loop = 2000000; +static long loop = 1000000; static char *str_timeout; -static int timeout = 240; +static int timeout; + +#define CALLIBRATE_LOOPS 120000000 + +static int callibrate(void) +{ + int i; + struct timespec start, stop; + long long diff; + + for (i = 0; i < CALLIBRATE_LOOPS; i++) + __asm__ __volatile__ ("" : "+g" (i) : :); + + SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC_RAW, &start); + + for (i = 0; i < CALLIBRATE_LOOPS; i++) + __asm__ __volatile__ ("" : "+g" (i) : :); + + SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC_RAW, &stop); + + diff = tst_timespec_diff_us(stop, start); + + tst_res(TINFO, "CPU did %i loops in %llius", CALLIBRATE_LOOPS, diff); + + return diff; +} static int wait_for_pid(pid_t pid) { @@ -49,20 +75,45 @@ again: static void setup(void) { cpu_set_t mask; + int cpu = 0; + long ncpus = tst_ncpus_conf(); + + if (tst_check_preempt_rt()) + tst_brk(TCONF, "This test is not designed for the RT kernel"); CPU_ZERO(&mask); - CPU_SET(0, &mask); + /* Restrict test to a single cpu */ + if (sched_getaffinity(0, sizeof(mask), &mask) < 0) + tst_brk(TBROK | TERRNO, "sched_getaffinity() failed"); - TST_EXP_POSITIVE(sched_setaffinity(0, sizeof(mask), &mask)); + if (CPU_COUNT(&mask) == 0) + tst_brk(TBROK, "No cpus available"); + + while (CPU_ISSET(cpu, &mask) == 0 && cpu < ncpus) + cpu++; + + CPU_ZERO(&mask); + + CPU_SET(cpu, &mask); + + tst_res(TINFO, "Setting affinity to CPU %d", cpu); + + if (sched_setaffinity(0, sizeof(mask), &mask) < 0) + tst_brk(TBROK | TERRNO, "sched_setaffinity() failed"); if (tst_parse_long(str_loop, &loop, 1, LONG_MAX)) tst_brk(TBROK, "Invalid number of loop number '%s'", str_loop); if (tst_parse_int(str_timeout, &timeout, 1, INT_MAX)) tst_brk(TBROK, "Invalid number of timeout '%s'", str_timeout); + else + timeout = callibrate() / 1000; - tst_set_max_runtime(timeout); + if (tst_has_slow_kconfig()) + tst_brk(TCONF, "Skip test due to slow kernel configuration"); + + tst_set_runtime(timeout); } static void handler(int sig LTP_ATTRIBUTE_UNUSED) @@ -97,7 +148,13 @@ static void do_test(void) sleep(1); SAFE_KILL(child_pid, SIGTERM); - TST_EXP_PASS(wait_for_pid(child_pid)); + + if (!tst_remaining_runtime()) + tst_res(TFAIL, "Scheduler starvation reproduced"); + else + tst_res(TPASS, "Haven't reproduced scheduler starvation"); + + TST_EXP_PASS_SILENT(wait_for_pid(child_pid)); } static struct tst_test test = { diff --git a/testcases/kernel/sched/hyperthreading/ht_affinity/ht_affinity.c b/testcases/kernel/sched/hyperthreading/ht_affinity/ht_affinity.c index f6e9f274..3c2fe1bf 100755 --- a/testcases/kernel/sched/hyperthreading/ht_affinity/ht_affinity.c +++ b/testcases/kernel/sched/hyperthreading/ht_affinity/ht_affinity.c @@ -67,7 +67,7 @@ int HT_SetAffinity(void) tst_resm(TINFO, "Set test process affinity."); printf("mask: %x\n", mask); - sched_setaffinity(pid, sizeof(unsigned long), &mask); + sched_setaffinity(pid, sizeof(mask), &mask); for (j = 0; j < 10; j++) { for (k = 0; k < 10; k++) { @@ -95,7 +95,7 @@ int HT_SetAffinity(void) tst_resm(TINFO, "Set test process affinity."); printf("mask: %x\n", mask); - sched_setaffinity(pid, sizeof(unsigned long), &mask); + sched_setaffinity(pid, sizeof(mask), &mask); for (j = 0; j < 10; j++) { for (k = 0; k < 10; k++) { diff --git a/testcases/kernel/sched/nptl/nptl01.c b/testcases/kernel/sched/nptl/nptl01.c index 1389249b..e80f70cb 100755 --- a/testcases/kernel/sched/nptl/nptl01.c +++ b/testcases/kernel/sched/nptl/nptl01.c @@ -55,7 +55,7 @@ void cleanup(); pthread_mutex_t req; pthread_mutex_t ack; -pthread_mutex_t wait; +pthread_mutex_t waitx; pthread_cond_t parent; pthread_cond_t child; int idle_count = 0; @@ -173,9 +173,9 @@ void *run(void *arg) call_mutex_lock(&req, buf, sizeof(buf)); call_mutex_unlock(&ack, buf, sizeof(buf)); - call_mutex_lock(&wait, buf, sizeof(buf)); + call_mutex_lock(&waitx, buf, sizeof(buf)); call_cond_signal(&parent, buf, sizeof(buf)); - call_mutex_unlock(&wait, buf, sizeof(buf)); + call_mutex_unlock(&waitx, buf, sizeof(buf)); call_cond_wait(&child, &req, buf, sizeof(buf)); call_mutex_unlock(&req, buf, sizeof(buf)); @@ -250,7 +250,7 @@ int main(int argc, char **argv) call_mutex_init(&req, buf, sizeof(buf)); call_mutex_init(&ack, buf, sizeof(buf)); - call_mutex_init(&wait, buf, sizeof(buf)); + call_mutex_init(&waitx, buf, sizeof(buf)); call_cond_init(&parent, buf, sizeof(buf)); call_cond_init(&child, buf, sizeof(buf)); @@ -266,7 +266,7 @@ int main(int argc, char **argv) idle_count = 0; call_mutex_unlock(&ack, buf, sizeof(buf)); - do_timedwait(&parent, &wait, buf, sizeof(buf), i); + do_timedwait(&parent, &waitx, buf, sizeof(buf), i); call_mutex_lock(&req, buf, sizeof(buf)); call_cond_signal(&child, buf, sizeof(buf)); diff --git a/testcases/kernel/sched/sched_stress/sched_driver.c b/testcases/kernel/sched/sched_stress/sched_driver.c index 61573d78..5b8c187f 100755 --- a/testcases/kernel/sched/sched_stress/sched_driver.c +++ b/testcases/kernel/sched/sched_stress/sched_driver.c @@ -136,7 +136,7 @@ int debug = 0; /* * Function prototypes */ -void startup(long); +void startup(time_t); int start_testcase(char *, char *, char *, char *, char *, char *); int process_slots_in_use(); int available_user_process_slots(); @@ -251,7 +251,7 @@ int main(int argc, char **argv) * information to the screen and . It also initializes the * * process id list and other global variables. * *-----------------------------------------------------------------------*/ -void startup(long start_time) +void startup(time_t start_time) { char tempbuffer[50]; /* temporary buffer to hold names */ @@ -734,7 +734,7 @@ void kill_short_term_testcases() void finishup(start_time) long start_time; /* starting time to calculate elapsed time */ { - long end_time; /* time when program finished */ + time_t end_time; /* time when program finished */ /* * Get the end time and calculate elapsed time; write all this out diff --git a/testcases/kernel/sched/sysctl/.gitignore b/testcases/kernel/sched/sysctl/.gitignore new file mode 100644 index 00000000..29b859b8 --- /dev/null +++ b/testcases/kernel/sched/sysctl/.gitignore @@ -0,0 +1 @@ +proc_sched_rt01 diff --git a/testcases/kernel/sched/sysctl/Makefile b/testcases/kernel/sched/sysctl/Makefile new file mode 100644 index 00000000..18896b6f --- /dev/null +++ b/testcases/kernel/sched/sysctl/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/sched/sysctl/proc_sched_rt01.c b/testcases/kernel/sched/sysctl/proc_sched_rt01.c new file mode 100644 index 00000000..df57ebcd --- /dev/null +++ b/testcases/kernel/sched/sysctl/proc_sched_rt01.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Cyril Hrubis + */ + +/*\ + * Sanity tests for the /proc/sys/kernel/sched_r* files. + * + * - The sched_rt_period_us range is 1 to INT_MAX + * try invalid values and check for EINVAL + * + * - The sched_rt_runtime_us range is -1 to INT_MAX + * try invalid values and check for EINVAL + * + * - The sched_rt_runtime_us must be less or equal to sched_rt_period_us + * + * - Reset sched_rr_timeslice_ms to default value by writing -1 and check that + * we get the default value on next read. + * + * This is a regression test for a commits: + * + * - c1fc6484e1fb ("sched/rt: sysctl_sched_rr_timeslice show default timeslice after reset") + * - 079be8fc6309 ("sched/rt: Disallow writing invalid values to sched_rt_period_us") + */ + +#include +#include "tst_test.h" + +#define RT_PERIOD_US "/proc/sys/kernel/sched_rt_period_us" +#define RT_RUNTIME_US "/proc/sys/kernel/sched_rt_runtime_us" +#define RR_TIMESLICE_MS "/proc/sys/kernel/sched_rr_timeslice_ms" + +static int period_fd; +static int runtime_fd; + +static void rr_timeslice_ms_reset(void) +{ + long timeslice_ms; + + SAFE_FILE_PRINTF(RR_TIMESLICE_MS, "-1"); + SAFE_FILE_SCANF(RR_TIMESLICE_MS, "%li", ×lice_ms); + + TST_EXP_EXPR(timeslice_ms > 0, + "timeslice_ms > 0 after reset to default"); +} + +static void rt_period_us_einval(void) +{ + TST_EXP_FAIL(write(period_fd, "0", 2), EINVAL, + "echo 0 > "RT_PERIOD_US); + TST_EXP_FAIL(write(period_fd, "-1", 2), EINVAL, + "echo -1 > "RT_PERIOD_US); +} + +static void rt_runtime_us_einval(void) +{ + TST_EXP_FAIL(write(runtime_fd, "-2", 2), EINVAL, + "echo -2 > "RT_RUNTIME_US); +} + +static void rt_runtime_us_le_period_us(void) +{ + int period_us; + char buf[32]; + + SAFE_FILE_SCANF(RT_PERIOD_US, "%i", &period_us); + + sprintf(buf, "%i", period_us+1); + + TST_EXP_FAIL(write(runtime_fd, buf, strlen(buf)), EINVAL, + "echo rt_period_us+1 > "RT_RUNTIME_US); +} + +static void verify_sched_proc(void) +{ + rr_timeslice_ms_reset(); + rt_period_us_einval(); + rt_runtime_us_einval(); + rt_runtime_us_le_period_us(); +} + +static void setup(void) +{ + period_fd = open(RT_PERIOD_US, O_RDWR); + runtime_fd = open(RT_RUNTIME_US, O_RDWR); +} + +static void cleanup(void) +{ + if (period_fd > 0) + SAFE_CLOSE(period_fd); + + if (runtime_fd > 0) + SAFE_CLOSE(runtime_fd); +} + +static struct tst_test test = { + .needs_root = 1, + .setup = setup, + .cleanup = cleanup, + .test_all = verify_sched_proc, + .tags = (struct tst_tag []) { + {"linux-git", "c1fc6484e1fb"}, + {"linux-git", "079be8fc6309"}, + {} + }, + .needs_kconfigs = (const char *[]) { + "CONFIG_SYSCTL", + NULL + }, +}; diff --git a/testcases/kernel/security/aslr/.gitignore b/testcases/kernel/security/aslr/.gitignore new file mode 100644 index 00000000..a15f88c3 --- /dev/null +++ b/testcases/kernel/security/aslr/.gitignore @@ -0,0 +1 @@ +aslr01 diff --git a/testcases/kernel/security/aslr/Makefile b/testcases/kernel/security/aslr/Makefile new file mode 100644 index 00000000..1457667c --- /dev/null +++ b/testcases/kernel/security/aslr/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024 Linux Test Project + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +CFLAGS += -D_GNU_SOURCE + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/security/aslr/aslr01.c b/testcases/kernel/security/aslr/aslr01.c new file mode 100644 index 00000000..6a396e29 --- /dev/null +++ b/testcases/kernel/security/aslr/aslr01.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC + */ + +/*\ + * Test that address space layout randomization (ASLR) is sufficiently random. + * A bug in dynamic library mmapping may reduce ASLR randomness if the library + * file is larger than hugepage size. In 32bit compat mode, this may + * completely disable ASLR and force large dynamic libraries to be loaded + * at fixed addresses. + * + * The issue may not be reproducible if hugepage support is missing or no + * sufficiently large library is loaded into the test program. If libc is not + * large enough, you may use `export LD_PRELOAD=...` to load another + * sufficiently large library. The export keyword is required because + * the checks are done on a subprocess. + * + * In normal mode, the test checks that library base address has a minimum + * number of random bits (configurable using the -b option). In strict mode, + * the test checks that library base address is aligned to regular pagesize + * (not hugepage) and the number of random bits is at least + * CONFIG_ARCH_MMAP_RND_BITS_MIN or the compat equivalent. The -b option is + * ignored. + */ + +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_kernel.h" +#include "tst_kconfig.h" +#include "tst_safe_stdio.h" + +/* Indices for aslr_kconfigs[] below */ +#define ASLR_HAVE_COMPAT 0 +#define ASLR_MINBITS 1 +#define ASLR_COMPAT_MINBITS 2 + +static int pagebits, minbits = 8; +static char *minbits_str, *strict_check; +static char lib_path[PATH_MAX]; +static FILE *ldd; + +static struct tst_kconfig_var aslr_kconfigs[] = { + TST_KCONFIG_INIT("CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS"), + TST_KCONFIG_INIT("CONFIG_ARCH_MMAP_RND_BITS_MIN"), + TST_KCONFIG_INIT("CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN"), +}; + +static int count_align_bits(size_t val) +{ + int ret = 0; + + for (; val && !(val & 0x1); val >>= 1, ret++) + ; + + return ret; +} + +static int count_bits(size_t val) +{ + int ret = 0; + + for (; val; val >>= 1) { + if (val & 1) + ret++; + } + + return ret; +} + +/* Extract library path and base address from a line of ldd output. */ +static int parse_mapping(const char *line, char *path, uint64_t *addr) +{ + int ret; + + line = strchr(line, '/'); + + if (!line) + return 0; + + ret = sscanf(line, "%s (0x%" PRIx64 ")", path, addr); + return ret >= 2; +} + +/* + * Run ldd on the test executable and pass each library/address to callback. + * If the callback returns non-zero, the reader loop will immediately exit. + */ +static void read_shared_libraries(int (*callback)(void*, const char*, uint64_t), + void *arg) +{ + char line[PATH_MAX], path[PATH_MAX]; + uint64_t addr; + int ret; + + sprintf(path, "ldd /proc/%d/exe", getpid()); + ldd = SAFE_POPEN(path, "r"); + + while (fgets(line, PATH_MAX, ldd)) { + if (*line && !feof(ldd) && line[strlen(line) - 1] != '\n') + tst_brk(TBROK, "Dynamic library entry too long"); + + if (!parse_mapping(line, path, &addr)) + continue; + + if (callback(arg, path, addr)) + break; + } + + while (fgets(line, PATH_MAX, ldd)) + ; + + ret = pclose(ldd); + ldd = NULL; + + if (!WIFEXITED(ret) || WEXITSTATUS(ret)) + tst_brk(TBROK, "Reading dynamic libraries failed"); +} + +static int find_large_lib_callback(void *arg, const char *path, + uint64_t addr LTP_ATTRIBUTE_UNUSED) +{ + size_t *libsize = arg; + struct stat statbuf; + + SAFE_STAT(path, &statbuf); + + if (*libsize < (size_t)statbuf.st_size) { + strcpy(lib_path, path); + *libsize = statbuf.st_size; + } + + return 0; +} + +static void find_large_lib(void) +{ + size_t hpsize, libsize = 0; + + read_shared_libraries(find_large_lib_callback, &libsize); + + if (!libsize) { + tst_brk(TCONF, + "No dynamic libraries loaded, please use LD_PRELOAD"); + } + + hpsize = tst_get_hugepage_size(); + tst_res(TINFO, "Largest loaded library: %s (%zu bytes)", lib_path, + libsize); + + if (!hpsize) { + tst_res(TCONF, "Hugepage support appears to be missing"); + } else if (libsize < hpsize) { + tst_res(TCONF, "The largest dynamic library is smaller than hugepage size, " + "please use LD_PRELOAD to add larger library"); + } +} + +static int get_lib_address_callback(void *arg, const char *path, uint64_t addr) +{ + uint64_t *out_addr = arg; + + if (!strcmp(path, lib_path)) { + *out_addr = addr; + return 1; + } + + return 0; +} + +static void setup(void) +{ + int compat = tst_is_compat_mode(); + const char *kconf_minbits, *minbits_path; + + pagebits = count_align_bits(getpagesize()); + tst_kconfig_read(aslr_kconfigs, ARRAY_SIZE(aslr_kconfigs)); + + if (compat && aslr_kconfigs[ASLR_HAVE_COMPAT].choice != 'y') + tst_brk(TCONF, "ASLR not supported in compat mode"); + + if (!strict_check && tst_parse_int(minbits_str, &minbits, 1, 64)) + tst_brk(TBROK, "Invalid bit count argument '%s'", minbits_str); + + if (strict_check) { + if (compat) { + kconf_minbits = aslr_kconfigs[ASLR_COMPAT_MINBITS].val; + minbits_path = "/proc/sys/vm/mmap_rnd_compat_bits"; + } else { + kconf_minbits = aslr_kconfigs[ASLR_MINBITS].val; + minbits_path = "/proc/sys/vm/mmap_rnd_bits"; + } + + /* + * Reading mmap_rnd_bits usually requires root privileges. + * Fall back to kernel config values if unprivileged. + */ + if (!access(minbits_path, R_OK)) + SAFE_FILE_SCANF(minbits_path, "%d", &minbits); + else if (!kconf_minbits) + tst_brk(TBROK, "Cannot determine kernel ASLR min bits"); + else if (tst_parse_int(kconf_minbits, &minbits, 1, 64)) + tst_brk(TBROK, "Invalid kernel ASLR min bits value"); + } + + find_large_lib(); +} + +static void run(void) +{ + uint64_t rndbits = 0, fixbits, addr; + int rndcount, aligncount, i; + + fixbits = ~rndbits; + + for (i = 0; i < 512; i++) { + addr = 0; + read_shared_libraries(get_lib_address_callback, &addr); + + if (!addr) { + tst_res(TWARN, "Library %s not found?!", lib_path); + continue; + } + + rndbits |= addr; + fixbits &= addr; + } + + rndcount = count_bits(rndbits & ~fixbits); + aligncount = count_align_bits(rndbits); + + if (rndcount < minbits) { + tst_res(TFAIL, + "Large lib base address has less than %d random bits", + minbits); + return; + } + + tst_res(TPASS, "Library address has %d random bits", rndcount); + tst_res(TINFO, "Library base address alignment: %d bits", aligncount); + + if (aligncount > pagebits) { + tst_res(strict_check ? TFAIL : TINFO, + "Base address alignment is higher than expected (%d)", + pagebits); + } +} + +static void cleanup(void) +{ + if (ldd) { + char buf[PATH_MAX]; + + while (fgets(buf, PATH_MAX, ldd)) + ; + + pclose(ldd); + } +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .options = (struct tst_option []) { + {"b:", &minbits_str, "Minimum ASLR random bits (default: 8)"}, + {"s", &strict_check, "Run in strict mode"}, + {} + }, + .needs_kconfigs = (const char *[]) { + "CONFIG_HAVE_ARCH_MMAP_RND_BITS=y", + NULL + }, + .needs_cmds = (const char *[]) { + "ldd", + NULL + }, +}; diff --git a/testcases/kernel/security/dirtyc0w_shmem/dirtyc0w_shmem.c b/testcases/kernel/security/dirtyc0w_shmem/dirtyc0w_shmem.c index f3d6e642..2c7ad00f 100644 --- a/testcases/kernel/security/dirtyc0w_shmem/dirtyc0w_shmem.c +++ b/testcases/kernel/security/dirtyc0w_shmem/dirtyc0w_shmem.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This is a regression test for a write race that allowed unprivileged programs * to change readonly files located on tmpfs/shmem on the system using * userfaultfd "minor fault handling" (CVE-2022-2590). @@ -106,9 +104,10 @@ static void cleanup(void) static struct tst_test test = { .needs_checkpoints = 1, + .child_needs_reinit =1, .forks_child = 1, .needs_root = 1, - .max_runtime = 120, + .runtime = 120, .setup = setup, .cleanup = cleanup, .test_all = dirtyc0w_shmem_test, diff --git a/testcases/kernel/security/dirtypipe/dirtypipe.c b/testcases/kernel/security/dirtypipe/dirtypipe.c index 9568a9b8..981793e6 100644 --- a/testcases/kernel/security/dirtypipe/dirtypipe.c +++ b/testcases/kernel/security/dirtypipe/dirtypipe.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2022 CM4all GmbH / IONOS SE * @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Proof-of-concept exploit for the Dirty Pipe * vulnerability (CVE-2022-0847) caused by an uninitialized * "pipe_buffer.flags" variable. It demonstrates how to overwrite any diff --git a/testcases/kernel/security/integrity/ima/README.md b/testcases/kernel/security/integrity/ima/README.md index 5b261a19..c5b3db1a 100755 --- a/testcases/kernel/security/integrity/ima/README.md +++ b/testcases/kernel/security/integrity/ima/README.md @@ -8,6 +8,18 @@ CONFIG_INTEGRITY=y CONFIG_IMA=y ``` +### Loading policy for testing (optional) +Setting environment variable `LTP_IMA_LOAD_POLICY=1` tries to load example +policy if available. This should be used only if tooling running LTP tests +allows to reboot afterwards because policy may be writable only once, e.g. +missing `CONFIG_IMA_WRITE_POLICY=y`, or policies can influence each other. + +Loading may fail due various reasons (e.g. previously mentioned missing +`CONFIG_IMA_WRITE_POLICY=y` and policy already loaded or when secure boot is +enabled and the kernel is configured with `CONFIG_IMA_ARCH_POLICY` enabled, an +`appraise func=POLICY_CHECK appraise_type=imasig` rule is loaded, requiring the +IMA policy itself to be signed). + ### IMA measurement tests `ima_measurements.sh` require builtin IMA tcb policy to be loaded (`ima_policy=tcb` kernel parameter). diff --git a/testcases/kernel/security/integrity/ima/datafiles/Makefile b/testcases/kernel/security/integrity/ima/datafiles/Makefile index 200fd3f4..2013bfc9 100755 --- a/testcases/kernel/security/integrity/ima/datafiles/Makefile +++ b/testcases/kernel/security/integrity/ima/datafiles/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) Linux Test Project, 2019-2020 +# Copyright (c) Linux Test Project, 2019-2025 # Copyright (c) 2020 Microsoft Corporation # Copyright (C) 2009, Cisco Systems Inc. # Ngie Cooper, July 2009 @@ -8,6 +8,6 @@ top_srcdir ?= ../../../../../.. include $(top_srcdir)/include/mk/env_pre.mk -SUBDIRS := ima_kexec ima_keys ima_policy ima_selinux +SUBDIRS := ima_kexec ima_keys ima_measurements ima_policy ima_selinux ima_violations include $(top_srcdir)/include/mk/generic_trunk_target.mk diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_measurements/Makefile b/testcases/kernel/security/integrity/ima/datafiles/ima_measurements/Makefile new file mode 100644 index 00000000..6317f2bf --- /dev/null +++ b/testcases/kernel/security/integrity/ima/datafiles/ima_measurements/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2025 + +top_srcdir ?= ../../../../../../.. + +include $(top_srcdir)/include/mk/env_pre.mk + +INSTALL_DIR := testcases/data/ima_measurements +INSTALL_TARGETS := *.policy + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_measurements/tcb.policy b/testcases/kernel/security/integrity/ima/datafiles/ima_measurements/tcb.policy new file mode 100644 index 00000000..1e4a932b --- /dev/null +++ b/testcases/kernel/security/integrity/ima/datafiles/ima_measurements/tcb.policy @@ -0,0 +1,20 @@ +dont_measure fsmagic=0x9fa0 +dont_measure fsmagic=0x62656572 +dont_measure fsmagic=0x64626720 +dont_measure fsmagic=0x1021994 func=FILE_CHECK +dont_measure fsmagic=0x1cd1 +dont_measure fsmagic=0x42494e4d +dont_measure fsmagic=0x73636673 +dont_measure fsmagic=0xf97cff8c +dont_measure fsmagic=0x43415d53 +dont_measure fsmagic=0x27e0eb +dont_measure fsmagic=0x63677270 +dont_measure fsmagic=0x6e736673 +dont_measure fsmagic=0xde5e81e4 +measure func=MMAP_CHECK mask=MAY_EXEC +measure func=BPRM_CHECK mask=MAY_EXEC +measure func=FILE_CHECK mask=^MAY_READ euid=0 +measure func=FILE_CHECK mask=^MAY_READ uid=0 +measure func=MODULE_CHECK +measure func=FIRMWARE_CHECK +measure func=POLICY_CHECK diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_policy/measure.policy b/testcases/kernel/security/integrity/ima/datafiles/ima_policy/measure.policy index 9976ddf2..8abd05fb 100755 --- a/testcases/kernel/security/integrity/ima/datafiles/ima_policy/measure.policy +++ b/testcases/kernel/security/integrity/ima/datafiles/ima_policy/measure.policy @@ -8,7 +8,7 @@ dont_measure fsmagic=0x62656572 # DEBUGFS_MAGIC dont_measure fsmagic=0x64626720 # TMPFS_MAGIC -dont_measure fsmagic=0x01021994 +dont_measure fsmagic=0x1021994 func=FILE_CHECK # SECURITYFS_MAGIC dont_measure fsmagic=0x73636673 measure func=FILE_MMAP mask=MAY_EXEC diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_violations/Makefile b/testcases/kernel/security/integrity/ima/datafiles/ima_violations/Makefile new file mode 100644 index 00000000..58d474f0 --- /dev/null +++ b/testcases/kernel/security/integrity/ima/datafiles/ima_violations/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2025 + +top_srcdir ?= ../../../../../../.. + +include $(top_srcdir)/include/mk/env_pre.mk + +INSTALL_DIR := testcases/data/ima_violations +INSTALL_TARGETS := *.policy + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_violations/violations.policy b/testcases/kernel/security/integrity/ima/datafiles/ima_violations/violations.policy new file mode 100644 index 00000000..466b8c5a --- /dev/null +++ b/testcases/kernel/security/integrity/ima/datafiles/ima_violations/violations.policy @@ -0,0 +1,2 @@ +measure func=FILE_CHECK mask=^MAY_READ euid=0 +measure func=FILE_CHECK mask=^MAY_READ uid=0 diff --git a/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c b/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c index 62468e0d..420b0c73 100755 --- a/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c +++ b/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c @@ -24,6 +24,7 @@ #if HAVE_LIBCRYPTO #include +#include #define MAX_EVENT_SIZE (1024*1024) #define EVENT_HEADER_SIZE 32 @@ -61,7 +62,11 @@ static void display_sha1_digest(unsigned char *pcr) static void do_test(void) { FILE *fp; +#if OPENSSL_VERSION_NUMBER > 0x030000000L + EVP_MD_CTX *c = NULL; +#else SHA_CTX c; +#endif int i; if (!file) @@ -85,12 +90,24 @@ static void do_test(void) } if (event.header.pcr < NUM_PCRS) { +#if OPENSSL_VERSION_NUMBER > 0x030000000L + if ((c = EVP_MD_CTX_new()) == NULL) + tst_brk(TBROK, "can't get new context"); + + EVP_DigestInit_ex(c, EVP_sha1(), NULL); + EVP_DigestUpdate(c, pcr[event.header.pcr].digest, + SHA_DIGEST_LENGTH); + EVP_DigestUpdate(c, event.header.digest, SHA_DIGEST_LENGTH); + EVP_DigestFinal_ex(c, pcr[event.header.pcr].digest, NULL); + EVP_MD_CTX_free(c); +#else SHA1_Init(&c); SHA1_Update(&c, pcr[event.header.pcr].digest, SHA_DIGEST_LENGTH); SHA1_Update(&c, event.header.digest, SHA_DIGEST_LENGTH); SHA1_Final(pcr[event.header.pcr].digest, &c); +#endif } #if MAX_EVENT_DATA_SIZE < USHRT_MAX @@ -99,7 +116,8 @@ static void do_test(void) break; } #endif - fread(event.data, event.header.len, 1, fp); + if (fread(event.data, event.header.len, 1, fp) != 1) + tst_brk(TBROK, "failed to read 1 byte"); } SAFE_FCLOSE(fp); @@ -107,15 +125,30 @@ static void do_test(void) /* Extend the boot aggregate with the pseudo PCR digest values */ memset(&boot_aggregate, 0, SHA_DIGEST_LENGTH); + +#if OPENSSL_VERSION_NUMBER > 0x030000000L + EVP_DigestInit_ex(c, EVP_sha1(), NULL); +#else SHA1_Init(&c); +#endif + for (i = 0; i < NUM_PCRS; i++) { if (debug) { printf("PCR-%2.2x: ", i); display_sha1_digest(pcr[i].digest); } +#if OPENSSL_VERSION_NUMBER > 0x030000000L + EVP_DigestUpdate(c, pcr[i].digest, SHA_DIGEST_LENGTH); +#else SHA1_Update(&c, pcr[i].digest, SHA_DIGEST_LENGTH); +#endif } + +#if OPENSSL_VERSION_NUMBER > 0x030000000L + EVP_MD_CTX_free(c); +#else SHA1_Final(boot_aggregate, &c); +#endif printf("sha1:"); display_sha1_digest(boot_aggregate); diff --git a/testcases/kernel/security/integrity/ima/tests/evm_overlay.sh b/testcases/kernel/security/integrity/ima/tests/evm_overlay.sh index 12b2a28c..6a48f63a 100755 --- a/testcases/kernel/security/integrity/ima/tests/evm_overlay.sh +++ b/testcases/kernel/security/integrity/ima/tests/evm_overlay.sh @@ -9,6 +9,7 @@ TST_SETUP="setup" TST_CLEANUP="cleanup" TST_CNT=4 +REQUIRED_BUILTIN_POLICY="appraise_tcb" setup() { @@ -17,8 +18,6 @@ setup() [ -f "$EVM_FILE" ] || tst_brk TCONF "EVM not enabled in kernel" [ $(cat $EVM_FILE) -eq 1 ] || tst_brk TCONF "EVM not enabled for this boot" - require_ima_policy_cmdline "appraise_tcb" - lower="$TST_MNTPOINT/lower" upper="$TST_MNTPOINT/upper" work="$TST_MNTPOINT/work" diff --git a/testcases/kernel/security/integrity/ima/tests/ima_conditionals.sh b/testcases/kernel/security/integrity/ima/tests/ima_conditionals.sh index b59f330c..91256168 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_conditionals.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_conditionals.sh @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2021 VPI Engineering -# Copyright (c) 2021 Petr Vorel +# Copyright (c) 2021-2025 Petr Vorel # Author: Alex Henrie # # Verify that conditional rules work. @@ -10,8 +10,16 @@ # support") from v5.16. TST_NEEDS_CMDS="cat chgrp chown id sg sudo" +TST_SETUP="setup" TST_CNT=1 +setup() +{ + if check_need_signed_policy; then + tst_brk TCONF "policy have to be signed" + fi +} + verify_measurement() { local request="$1" @@ -22,6 +30,7 @@ verify_measurement() local value="$(id -u $user)" [ "$request" = 'gid' -o "$request" = 'fgroup' ] && value="$(id -g $user)" + # needs to be checked each run (not in setup) require_policy_writable ROD rm -f $test_file diff --git a/testcases/kernel/security/integrity/ima/tests/ima_kexec.sh b/testcases/kernel/security/integrity/ima/tests/ima_kexec.sh index 62f05f53..d6eb0829 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_kexec.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_kexec.sh @@ -1,19 +1,21 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2020 Microsoft Corporation -# Copyright (c) 2020 Petr Vorel +# Copyright (c) 2020-2025 Petr Vorel # Author: Lachlan Sneff # # Verify that kexec cmdline is measured correctly. # Test attempts to kexec the existing running kernel image. # To kexec a different kernel image export IMA_KEXEC_IMAGE=. +# Test requires example IMA policy loadable with LTP_IMA_LOAD_POLICY=1. TST_NEEDS_CMDS="grep kexec sed" TST_CNT=3 TST_SETUP="setup" +TST_MIN_KVER="5.3" IMA_KEXEC_IMAGE="${IMA_KEXEC_IMAGE:-/boot/vmlinuz-$(uname -r)}" -REQUIRED_POLICY='^measure.*func=KEXEC_CMDLINE' +REQUIRED_POLICY_CONTENT='kexec.policy' measure() { @@ -40,15 +42,38 @@ measure() setup() { - tst_res TINFO "using kernel $IMA_KEXEC_IMAGE" + local arch + + if [ ! -f "$IMA_KEXEC_IMAGE" ]; then + for arg in $(cat /proc/cmdline); do + if echo "$arg" |grep -q '^BOOT_IMAGE'; then + eval "$arg" + fi + done + + tst_res TINFO "using as kernel BOOT_IMAGE from /proc/cmdline: '$BOOT_IMAGE'" + + # replace grub partition, e.g. (hd0,gpt2) => /boot + if echo "$BOOT_IMAGE" |grep -q '(.d[0-9]'; then + echo "$BOOT_IMAGE" | sed 's|(.*,.*)/|/boot/|' + fi + + if [ -f "$BOOT_IMAGE" ]; then + IMA_KEXEC_IMAGE="$BOOT_IMAGE" + fi + fi if [ ! -f "$IMA_KEXEC_IMAGE" ]; then tst_brk TCONF "kernel image not found, specify path in \$IMA_KEXEC_IMAGE" fi - if check_policy_readable; then - require_ima_policy_content "$REQUIRED_POLICY" - policy_readable=1 + tst_res TINFO "using kernel $IMA_KEXEC_IMAGE" + + tst_res TINFO "$(kexec -v)" + + REUSE_CMDLINE_SUPPORTED= + if kexec -h 2>&1 | grep -q reuse-cmdline; then + REUSE_CMDLINE_SUPPORTED=1 fi } @@ -78,11 +103,14 @@ kexec_test() { local param="$1" local cmdline="$2" - local res=TFAIL local kexec_cmd kexec_cmd="$param=$cmdline" if [ "$param" = '--reuse-cmdline' ]; then + if [ "$REUSE_CMDLINE_SUPPORTED" != 1 ]; then + tst_res TCONF "--reuse-cmdline not supported" + return + fi cmdline="$(sed 's/BOOT_IMAGE=[^ ]* //' /proc/cmdline)" kexec_cmd="$param" fi @@ -96,13 +124,10 @@ kexec_test() ROD kexec -su if ! measure "$cmdline"; then - if [ "$policy_readable" != 1 ]; then - tst_res TWARN "policy not readable, it might not contain required policy '$REQUIRED_POLICY'" - res=TBROK - fi - tst_brk $res "unable to find a correct measurement" + tst_res $IMA_FAIL "unable to find a correct measurement" + else + tst_res TPASS "kexec cmdline was measured correctly" fi - tst_res TPASS "kexec cmdline was measured correctly" } test() diff --git a/testcases/kernel/security/integrity/ima/tests/ima_keys.sh b/testcases/kernel/security/integrity/ima/tests/ima_keys.sh index 793908d4..4af21b4a 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_keys.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_keys.sh @@ -1,23 +1,31 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2020 Microsoft Corporation -# Copyright (c) 2020-2021 Petr Vorel +# Copyright (c) 2020-2025 Petr Vorel # Author: Lachlan Sneff # # Verify that keys are measured correctly based on policy. +# Test requires example IMA policy loadable with LTP_IMA_LOAD_POLICY=1. TST_NEEDS_CMDS="cmp cut grep sed" TST_CNT=2 TST_SETUP=setup TST_CLEANUP=cleanup +TST_MIN_KVER="5.6" -FUNC_KEYCHECK='func=KEY_CHECK' -REQUIRED_POLICY="^measure.*$FUNC_KEYCHECK" +REQUIRED_POLICY_CONTENT='keycheck.policy' setup() { - require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt - require_valid_policy_template + local line + + require_policy_readable + + while read line; do + if echo $line | grep -q 'template=' && ! echo $line | grep -q 'template=ima-buf'; then + tst_brk TCONF "only template=ima-buf can be specified for KEY_CHECK" + fi + done < $IMA_POLICY } cleanup() @@ -25,38 +33,19 @@ cleanup() tst_is_num $KEYRING_ID && keyctl clear $KEYRING_ID } -require_valid_policy_template() -{ - while read line; do - if echo $line | grep -q 'template=' && ! echo $line | grep -q 'template=ima-buf'; then - tst_brk TCONF "only template=ima-buf can be specified for KEY_CHECK" - fi - done < $TST_TMPDIR/policy.txt -} - -check_keys_policy() -{ - local pattern="$1" - - if ! grep -E "$pattern" $TST_TMPDIR/policy.txt; then - tst_res TCONF "IMA policy must specify $pattern, $FUNC_KEYCHECK" - return 1 - fi - return 0 -} - # Based on https://lkml.org/lkml/2019/12/13/564. # (450d0fd51564 - "IMA: Call workqueue functions to measure queued keys") test1() { local keycheck_lines i keyrings templates local pattern='keyrings=[^[:space:]]+' - local test_file="file.txt" tmp_file="file2.txt" + local test_file="file.txt" tst_res TINFO "verify key measurement for keyrings and templates specified in IMA policy" - check_keys_policy "$pattern" > $tmp_file || return - keycheck_lines=$(cat $tmp_file) + keycheck_lines=$(grep -E "$pattern" $IMA_POLICY) + tst_res TINFO "keycheck_lines: '$keycheck_lines'" + keyrings=$(for i in $keycheck_lines; do echo "$i" | grep "keyrings" | \ sed "s/\./\\\./g" | cut -d'=' -f2; done | sed ':a;N;$!ba;s/\n/|/g') if [ -z "$keyrings" ]; then @@ -86,7 +75,7 @@ test1() fi if [ "$digest" != "$expected_digest" ]; then - tst_res TFAIL "incorrect digest was found for $keyring keyring" + tst_res $IMA_FAIL "incorrect digest was found for $keyring keyring" return fi done @@ -104,13 +93,10 @@ test2() local cert_file="$TST_DATAROOT/x509_ima.der" local keyring_name="key_import_test" - local pattern="keyrings=[^[:space:]]*$keyring_name" local temp_file="file.txt" tst_res TINFO "verify measurement of certificate imported into a keyring" - check_keys_policy "$pattern" >/dev/null || return - KEYRING_ID=$(keyctl newring $keyring_name @s) || \ tst_brk TBROK "unable to create a new keyring" @@ -125,19 +111,19 @@ test2() tst_hexdump -d > $temp_file if [ ! -s $temp_file ]; then - tst_res TFAIL "keyring $keyring_name not found in $ASCII_MEASUREMENTS" + tst_res $IMA_FAIL "keyring $keyring_name not found in $ASCII_MEASUREMENTS" return fi if ! openssl x509 -in $temp_file -inform der > /dev/null; then - tst_res TFAIL "logged certificate is not a valid x509 certificate" + tst_res $IMA_FAIL "logged certificate is not a valid x509 certificate" return fi if cmp -s $temp_file $cert_file; then tst_res TPASS "logged certificate matches the original" else - tst_res TFAIL "logged certificate does not match original" + tst_res $IMA_FAIL "logged certificate does not match original" fi } diff --git a/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh b/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh index 1da2aa6a..60350f39 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh @@ -1,19 +1,20 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2009 IBM Corporation -# Copyright (c) 2018-2021 Petr Vorel +# Copyright (c) 2018-2025 Petr Vorel # Author: Mimi Zohar # # Verify that measurements are added to the measurement list based on policy. +# Test requires either ima_policy=tcb or example policy loadable with LTP_IMA_LOAD_POLICY=1. TST_NEEDS_CMDS="awk cut sed" TST_SETUP="setup" TST_CNT=3 +REQUIRED_BUILTIN_POLICY="tcb" +REQUIRED_POLICY_CONTENT='tcb.policy' setup() { - require_ima_policy_cmdline "tcb" - TEST_FILE="$PWD/test.txt" [ -f "$IMA_POLICY" ] || tst_res TINFO "not using default policy" } @@ -70,11 +71,17 @@ test3() local user="nobody" local dir="$PWD/user" local file="$dir/test.txt" + local cmd="grep $file $ASCII_MEASUREMENTS" # Default policy does not measure user files tst_res TINFO "verify not measuring user files" tst_check_cmds sudo || return + if [ "$IMA_MISSING_POLICY_CONTENT" = 1 ]; then + tst_res TCONF "test requires specific policy, try load it with LTP_IMA_LOAD_POLICY=1" + return + fi + if ! id $user >/dev/null 2>/dev/null; then tst_res TCONF "missing system user $user (wrong installation)" return @@ -87,7 +94,11 @@ test3() sudo -n -u $user sh -c "echo $(cat /proc/uptime) user file > $file; cat $file > /dev/null" cd .. - EXPECT_FAIL "grep $file $ASCII_MEASUREMENTS" + if ! tst_rod "$cmd" 2> /dev/null; then + tst_res TPASS "$cmd failed as expected" + else + tst_res $IMA_FAIL "$cmd passed unexpectedly" + fi } . ima_setup.sh diff --git a/testcases/kernel/security/integrity/ima/tests/ima_policy.sh b/testcases/kernel/security/integrity/ima/tests/ima_policy.sh index af1fb002..d66f261a 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_policy.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_policy.sh @@ -20,6 +20,9 @@ setup() [ -f $INVALID_POLICY ] || tst_brk TCONF "missing $INVALID_POLICY" } +# NOTE: function spaws a new process, therefore it should not call tst_brk() +# (or otherwise exit a test), because that calls ima_cleanup() twice (which +# breaks umount on TMPDIR or removing TMPDIR). load_policy() { local ret @@ -64,12 +67,15 @@ test2() load_policy $VALID_POLICY & p2=$! wait "$p1"; rc1=$? wait "$p2"; rc2=$? + if [ $rc1 -eq 0 ] && [ $rc2 -eq 0 ]; then tst_res TFAIL "policy opened concurrently" elif [ $rc1 -eq 0 ] || [ $rc2 -eq 0 ]; then tst_res TPASS "policy was loaded just by one process and able to loaded multiple times" + elif check_need_signed_policy; then + tst_res TCONF "policy have to be signed" else - tst_res TFAIL "problem loading or extending policy (may require policy to be signed)" + tst_res TFAIL "problem loading or extending policy" fi } diff --git a/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh index f6e39282..1a0de21e 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh @@ -1,9 +1,12 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2021 Microsoft Corporation +# Copyright (c) Linux Test Project, 2021-2025 # Author: Lakshmi Ramasubramanian # # Verify measurement of SELinux policy hash and state. +# Test requires ima_policy=critical_data kernel command line and example IMA +# policy loadable with LTP_IMA_LOAD_POLICY=1. # # Relevant kernel commits: # * fdd1ffe8a812 ("selinux: include a consumer of the new IMA critical data hook") @@ -12,16 +15,16 @@ TST_NEEDS_CMDS="awk cut grep tail" TST_CNT=2 TST_SETUP="setup" +TST_MIN_KVER="5.12" -FUNC_CRITICAL_DATA='func=CRITICAL_DATA' -REQUIRED_POLICY="^measure.*$FUNC_CRITICAL_DATA" +REQUIRED_POLICY_CONTENT='selinux.policy' setup() { SELINUX_DIR=$(tst_get_selinux_dir) [ "$SELINUX_DIR" ] || tst_brk TCONF "SELinux is not enabled" - require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt + require_ima_policy_cmdline "critical_data" } # Format of the measured SELinux state data. @@ -44,7 +47,7 @@ validate_policy_capabilities() measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}') expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap") if [ "$measured_value" != "$expected_value" ]; then - tst_res TFAIL "$measured_cap: expected: $expected_value, got: $digest" + tst_res $IMA_FAIL "$measured_cap: expected: $expected_value, got: $digest" return fi @@ -74,7 +77,7 @@ test1() # in kernel memory for SELinux line=$(grep -E "selinux-policy-hash" $ASCII_MEASUREMENTS | tail -1) if [ -z "$line" ]; then - tst_res TFAIL "SELinux policy hash not measured" + tst_res $IMA_FAIL "SELinux policy hash not measured" return fi @@ -85,7 +88,7 @@ test1() tst_brk TCONF "cannot compute digest for $algorithm" if [ "$policy_digest" != "$expected_policy_digest" ]; then - tst_res TFAIL "Digest mismatch: expected: $expected_policy_digest, got: $policy_digest" + tst_res $IMA_FAIL "Digest mismatch: expected: $expected_policy_digest, got: $policy_digest" return fi @@ -115,7 +118,7 @@ test2() # state matches that currently set for SELinux line=$(grep -E "selinux-state" $ASCII_MEASUREMENTS | tail -1) if [ -z "$line" ]; then - tst_res TFAIL "SELinux state not measured" + tst_res $IMA_FAIL "SELinux state not measured" return fi @@ -128,7 +131,7 @@ test2() tst_brk TCONF "cannot compute digest for $algorithm" if [ "$digest" != "$expected_digest" ]; then - tst_res TFAIL "digest mismatch: expected: $expected_digest, got: $digest" + tst_res $IMA_FAIL "digest mismatch: expected: $expected_digest, got: $digest" return fi @@ -145,20 +148,20 @@ test2() enforced_value=$(echo $measured_data | awk -F'[=;]' '{print $4}') expected_enforced_value=$(cat $SELINUX_DIR/enforce) if [ "$expected_enforced_value" != "$enforced_value" ]; then - tst_res TFAIL "enforce: expected: $expected_enforced_value, got: $enforced_value" + tst_res $IMA_FAIL "enforce: expected: $expected_enforced_value, got: $enforced_value" return fi checkreqprot_value=$(echo $measured_data | awk -F'[=;]' '{print $6}') expected_checkreqprot_value=$(cat $SELINUX_DIR/checkreqprot) if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ]; then - tst_res TFAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value" + tst_res $IMA_FAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value" return fi initialized_value=$(echo $measured_data | awk -F'[=;]' '{print $2}') if [ "$initialized_value" != "1" ]; then - tst_res TFAIL "initialized: expected 1, got: $initialized_value" + tst_res $IMA_FAIL "initialized: expected 1, got: $initialized_value" return fi diff --git a/testcases/kernel/security/integrity/ima/tests/ima_setup.sh b/testcases/kernel/security/integrity/ima/tests/ima_setup.sh index df3fc560..2a7d6518 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_setup.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_setup.sh @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2009 IBM Corporation -# Copyright (c) 2018-2020 Petr Vorel +# Copyright (c) 2018-2025 Petr Vorel # Author: Mimi Zohar TST_TESTFUNC="test" @@ -11,6 +11,7 @@ TST_CLEANUP_CALLER="$TST_CLEANUP" TST_CLEANUP="ima_cleanup" TST_NEEDS_ROOT=1 TST_MOUNT_DEVICE=1 +TST_SKIP_LSM_WARNINGS=1 # TST_MOUNT_DEVICE can be unset, therefore specify explicitly TST_NEEDS_TMPDIR=1 @@ -19,6 +20,9 @@ SYSFS="/sys" UMOUNT= TST_FS_TYPE="ext3" +IMA_FAIL="TFAIL" +IMA_BROK="TBROK" + # TODO: find support for rmd128 rmd256 rmd320 wp256 wp384 tgr128 tgr160 compute_digest() { @@ -72,34 +76,28 @@ require_policy_readable() fi } +check_policy_writable() +{ + [ -f $IMA_POLICY ] || return 1 + # workaround for kernels < v4.18 without fix + # ffb122de9a60b ("ima: Reflect correct permissions for policy") + echo "" 2> log > $IMA_POLICY + grep -q "Device or resource busy" log && return 1 + return 0 +} + require_policy_writable() { - local err="IMA policy already loaded and kernel not configured to enable multiple writes to it (need CONFIG_IMA_WRITE_POLICY=y)" - - [ -f $IMA_POLICY ] || tst_brk TCONF "$err" - # CONFIG_IMA_READ_POLICY - echo "" 2> log > $IMA_POLICY - grep -q "Device or resource busy" log && tst_brk TCONF "$err" + check_policy_writable || tst_brk TCONF \ + "IMA policy already loaded and kernel not configured to enable multiple writes to it (need CONFIG_IMA_WRITE_POLICY=y)" } check_ima_policy_content() { local pattern="$1" - local grep_params="${2--q}" check_policy_readable || return 1 - grep $grep_params "$pattern" $IMA_POLICY -} - -require_ima_policy_content() -{ - local pattern="$1" - local grep_params="${2--q}" - - require_policy_readable - if ! grep $grep_params "$pattern" $IMA_POLICY; then - tst_brk TCONF "IMA policy does not specify '$pattern'" - fi + grep -q "$pattern" $IMA_POLICY } check_ima_policy_cmdline() @@ -121,7 +119,7 @@ require_ima_policy_cmdline() local policy="$1" check_ima_policy_cmdline $policy || \ - tst_brk TCONF "IMA measurement tests require builtin IMA $policy policy (e.g. ima_policy=$policy kernel parameter)" + tst_brk TCONF "test requires builtin IMA $policy policy (e.g. ima_policy=$policy kernel command line parameter)" } mount_helper() @@ -158,8 +156,103 @@ print_ima_config() tst_res TINFO "/proc/cmdline: $(cat /proc/cmdline)" } +# Check for required +# 1) IMA builtin policy (based on /proc/cmdline) +# 2) IMA policy content (actual content of /sys/kernel/security/ima/policy) +# When missing CONFIG_IMA_READ_POLICY=y on required policy convert: test, but convert TFAIL => TCONF. +# $REQUIRED_POLICY_CONTENT: file with required IMA policy +# $REQUIRED_BUILTIN_POLICY: IMA policy specified as kernel cmdline +# return: 1 if need to load policy 0 otherwise +verify_ima_policy() +{ + local check_content line + local file="$TST_DATAROOT/$REQUIRED_POLICY_CONTENT" + + if [ -z "$REQUIRED_POLICY_CONTENT" -a -z "$REQUIRED_BUILTIN_POLICY" ]; then + return 0 + fi + + if [ -n "$REQUIRED_POLICY_CONTENT" ]; then + check_content=1 + if [ -n "$REQUIRED_BUILTIN_POLICY" ] && check_ima_policy_cmdline "$REQUIRED_BUILTIN_POLICY"; then + tst_res TINFO "booted with IMA policy: $REQUIRED_BUILTIN_POLICY" + return 0 + fi + elif [ -n "$REQUIRED_BUILTIN_POLICY" ]; then + require_ima_policy_cmdline "$REQUIRED_BUILTIN_POLICY" + fi + + if [ "$check_content" = 1 ]; then + [ -e $file ] || tst_brk TBROK "policy file '$file' does not exist (LTPROOT=$LTPROOT)" + tst_res TINFO "test requires IMA policy:" + cat $file + if check_policy_readable; then + # check IMA policy content + while read line; do + if ! grep -q "$line" $IMA_POLICY; then + tst_res TINFO "WARNING: missing required policy content: '$line'" + IMA_MISSING_POLICY_CONTENT=1 + return 1 + fi + done < $file + tst_res TINFO "SUT has required policy content" + else + tst_res TINFO "WARNING: policy is not readable, failure will be treated as TCONF" + IMA_FAIL="TCONF" + IMA_BROK="TCONF" + return 1 + fi + fi + return 0 +} + +load_ima_policy() +{ + local file="$TST_DATAROOT/$REQUIRED_POLICY_CONTENT" + local ret + + if [ "$LTP_IMA_LOAD_POLICY" != 1 ]; then + if [ "$IMA_MISSING_POLICY_CONTENT" = 1 ]; then + tst_brk TCONF "missing required policy, example policy can be loaded with LTP_IMA_LOAD_POLICY=1" + fi + return + fi + + tst_res TINFO "trying to load '$file' policy:" + cat $file + if ! check_policy_writable; then + tst_res TINFO "WARNING: IMA policy already loaded and kernel not configured to enable multiple writes to it (need CONFIG_IMA_WRITE_POLICY=y), reboot required, failures will be treated as TCONF" + IMA_FAIL="TCONF" + IMA_BROK="TCONF" + LTP_IMA_LOAD_POLICY= + return + fi + + cat "$file" 2> log > $IMA_POLICY + ret=$? + if grep -q "Device or resource busy" log; then + tst_brk TBROK "loading policy failed" + fi + + if grep -q "write error: Permission denied" log; then + tst_brk TCONF "loading unsigned policy failed" + fi + + if [ $ret -ne 0 ]; then + tst_brk TBROK "loading policy failed" + fi + + IMA_POLICY_LOADED=1 + + tst_res TINFO "example policy successfully loaded" + IMA_FAIL="TFAIL" + IMA_BROK="TBROK" +} + ima_setup() { + local load_policy + SECURITYFS="$(mount_helper securityfs $SYSFS/kernel/security)" IMA_DIR="$SECURITYFS/ima" @@ -180,7 +273,16 @@ ima_setup() cd "$TST_MNTPOINT" fi + verify_ima_policy + load_policy=$? + + # Run setup in case of TCONF before loading policy [ -n "$TST_SETUP_CALLER" ] && $TST_SETUP_CALLER + + if [ "$load_policy" = 1 ]; then + load_ima_policy + fi + } ima_cleanup() @@ -192,6 +294,10 @@ ima_cleanup() for dir in $UMOUNT; do umount $dir done + + if [ "$IMA_POLICY_LOADED" = 1 ]; then + tst_res TINFO "WARNING: policy loaded via LTP_IMA_LOAD_POLICY=1, reboot recommended" + fi } set_digest_index() @@ -204,7 +310,7 @@ set_digest_index() # parse digest index # https://www.kernel.org/doc/html/latest/security/IMA-templates.html#use case "$template" in - ima|ima-ng|ima-sig) DIGEST_INDEX=4 ;; + ima|ima-buf|ima-ng|ima-sig) DIGEST_INDEX=4 ;; *) # using ima_template_fmt kernel parameter local IFS="|" @@ -218,8 +324,10 @@ set_digest_index() done esac - [ -z "$DIGEST_INDEX" ] && tst_brk TCONF \ - "Cannot find digest index (template: '$template')" + if [ -z "$DIGEST_INDEX" ]; then + tst_res TWARN "Cannot find digest index (template: '$template')" + return 1 + fi } get_algorithm_digest() @@ -233,7 +341,13 @@ get_algorithm_digest() return 1 fi - [ -z "$DIGEST_INDEX" ] && set_digest_index + if [ -z "$DIGEST_INDEX" ]; then + set_digest_index + fi + if [ -z "$DIGEST_INDEX" ]; then + return 1 + fi + digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX) if [ -z "$digest" ]; then echo "digest not found (index: $DIGEST_INDEX, line: '$line')" @@ -267,18 +381,18 @@ get_algorithm_digest() ima_check() { local test_file="$1" - local algorithm digest expected_digest line tmp + local algorithm digest expected_digest line # need to read file to get updated $ASCII_MEASUREMENTS cat $test_file > /dev/null line="$(grep $test_file $ASCII_MEASUREMENTS | tail -1)" - if tmp=$(get_algorithm_digest "$line"); then - algorithm=$(echo "$tmp" | cut -d'|' -f1) - digest=$(echo "$tmp" | cut -d'|' -f2) + if get_algorithm_digest "$line" > tmp; then + algorithm=$(cat tmp | cut -d'|' -f1) + digest=$(cat tmp | cut -d'|' -f2) else - tst_res TBROK "failed to get algorithm/digest for '$test_file': $tmp" + tst_brk $IMA_BROK "failed to get algorithm/digest for '$test_file'" fi tst_res TINFO "computing digest for $algorithm algorithm" @@ -335,6 +449,13 @@ require_evmctl() fi } +# 56dc986a6b20b ("ima: require signed IMA policy when UEFI secure boot is enabled") # v6.5-rc4 +check_need_signed_policy() +{ + tst_secureboot_enabled && tst_kvcmp -ge '6.5' && tst_require_kconfigs \ + 'CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY' +} + # loop device is needed to use only for tmpfs TMPDIR="${TMPDIR:-/tmp}" if tst_supported_fs -d $TMPDIR -s "tmpfs"; then diff --git a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh index b675a20a..5d34d867 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh @@ -17,14 +17,14 @@ ERRMSG_TPM="TPM hardware support not enabled in kernel or no TPM chip found" setup() { local config="${KCONFIG_PATH:-/boot/config-$(uname -r)}" - local line tmp + local line read line < $ASCII_MEASUREMENTS - if tmp=$(get_algorithm_digest "$line"); then - ALGORITHM=$(echo "$tmp" | cut -d'|' -f1) - DIGEST=$(echo "$tmp" | cut -d'|' -f2) + if get_algorithm_digest "$line" > tmp; then + ALGORITHM=$(cat tmp | cut -d'|' -f1) + DIGEST=$(cat tmp | cut -d'|' -f2) else - tst_brk TBROK "failed to get algorithm/digest: $tmp" + tst_brk TBROK "failed to get algorithm/digest" fi tst_res TINFO "used algorithm: $ALGORITHM" @@ -159,7 +159,8 @@ get_pcr10_aggregate() $cmd > hash.txt 2>&1 ret=$? elif [ $ret -ne 0 -a "$MISSING_EVMCTL" = 1 ]; then - tst_brk TFAIL "evmctl failed $msg" + tst_res TFAIL "evmctl failed $msg" + return fi [ $ret -ne 0 ] && tst_res TWARN "evmctl failed, trying to continue $msg" diff --git a/testcases/kernel/security/integrity/ima/tests/ima_violations.sh b/testcases/kernel/security/integrity/ima/tests/ima_violations.sh index 0f710dea..1d2f1d94 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_violations.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_violations.sh @@ -1,14 +1,19 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2009 IBM Corporation -# Copyright (c) 2018-2020 Petr Vorel +# Copyright (c) 2018-2025 Petr Vorel # Author: Mimi Zohar # # Test whether ToMToU and open_writer violations invalidatethe PCR and are logged. +# test[4-6] test 6.15 commit 5b3cd801155f ("ima: limit the number of open-writers integrity violations") +# test[7-8] test 6.15 commit a414016218ca ("ima: limit the number of ToMToU integrity violations") TST_SETUP="setup" TST_CLEANUP="cleanup" -TST_CNT=3 +TST_CNT=8 + +REQUIRED_BUILTIN_POLICY="tcb" +REQUIRED_POLICY_CONTENT='violations.policy' setup() { @@ -23,9 +28,12 @@ setup() PRINTK_RATE_LIMIT=`sysctl -n kernel.printk_ratelimit` sysctl -wq kernel.printk_ratelimit=0 fi - [ -f "$LOG" ] || \ - tst_brk TBROK "log $LOG does not exist (bug in detection?)" + + if [ ! -e "$LOG" ]; then + tst_brk TCONF "log file not found, install auditd" + fi tst_res TINFO "using log $LOG" + exec 3< $LOG || tst_brk TBROK "failed to read log file" } cleanup() @@ -55,6 +63,17 @@ close_file_write() exec 4>&- } +open_file_write2() +{ + exec 5> $FILE || tst_brk TBROK "exec 5> $FILE failed" + echo 'test writing2' >&5 +} + +close_file_write2() +{ + exec 5>&- +} + get_count() { local search="$1" @@ -66,26 +85,32 @@ validate() local num_violations="$1" local count="$2" local search="$3" + local expected_violations="$4" local max_attempt=3 local count2 i num_violations_new for i in $(seq 1 $max_attempt); do read num_violations_new < $IMA_VIOLATIONS count2="$(get_count $search)" - if [ $(($num_violations_new - $num_violations)) -gt 0 ]; then + if [ -z "$expected_violations" -a $(($num_violations_new - $num_violations)) -gt 0 ] || \ + [ $(($num_violations_new - $num_violations)) -eq $expected_violations ]; then + [ -z "$expected_violations" ] && expected_violations=1 if [ $count2 -gt $count ]; then - tst_res TPASS "$search violation added" + tst_res TPASS "$expected_violations $search violation(s) added" return else tst_res TINFO "$search not found in $LOG ($i/$max_attempt attempt)..." tst_sleep 1s fi + elif [ $(($num_violations_new - $num_violations)) -gt 0 ]; then + tst_res $IMA_FAIL "$search too many violations added: $num_violations_new - $num_violations" + return else - tst_res TFAIL "$search violation not added" + tst_res $IMA_FAIL "$search violation not added" return fi done - tst_res TFAIL "$search not found in $LOG" + tst_res $IMA_FAIL "$search not found in $LOG" } test1() @@ -149,6 +174,128 @@ test3() tst_sleep 2s } +test4() +{ + tst_res TINFO "verify limiting single open writer violation" + + if tst_kvcmp -lt 6.15; then + tst_brk TCONF "Minimizing violations requires kernel 6.15 or newer" + fi + + local search="open_writers" + local count num_violations + + read num_violations < $IMA_VIOLATIONS + count="$(get_count $search)" + + open_file_write + open_file_read + close_file_read + + open_file_read + close_file_read + + close_file_write + + validate "$num_violations" "$count" "$search" 1 +} + +test5() +{ + tst_res TINFO "verify limiting multiple open writers violations" + + local search="open_writers" + local count num_violations + + read num_violations < $IMA_VIOLATIONS + count="$(get_count $search)" + + open_file_write + open_file_read + close_file_read + + open_file_write2 + open_file_read + close_file_read + close_file_write2 + + open_file_read + close_file_read + + close_file_write + + validate "$num_violations" "$count" "$search" 1 +} + +test6() +{ + tst_res TINFO "verify new open writer causes additional violation" + + local search="open_writers" + local count num_violations + + read num_violations < $IMA_VIOLATIONS + count="$(get_count $search)" + + open_file_write + open_file_read + close_file_read + + open_file_read + close_file_read + close_file_write + + open_file_write + open_file_read + close_file_read + close_file_write + validate "$num_violations" "$count" "$search" 2 +} + +test7() +{ + tst_res TINFO "verify limiting single open reader ToMToU violations" + + local search="ToMToU" + local count num_violations + + read num_violations < $IMA_VIOLATIONS + count="$(get_count $search)" + + open_file_read + open_file_write + close_file_write + + open_file_write + close_file_write + close_file_read + + validate "$num_violations" "$count" "$search" 1 +} + +test8() +{ + tst_res TINFO "verify new open reader causes additional violation" + + local search="ToMToU" + local count num_violations + + read num_violations < $IMA_VIOLATIONS + count="$(get_count $search)" + + open_file_read + open_file_write + close_file_write + close_file_read + + open_file_read + open_file_write + close_file_write + close_file_read + + validate "$num_violations" "$count" "$search" 2 +} + . ima_setup.sh . daemonlib.sh tst_run diff --git a/testcases/kernel/security/kallsyms/.gitignore b/testcases/kernel/security/kallsyms/.gitignore new file mode 100644 index 00000000..7074d4e2 --- /dev/null +++ b/testcases/kernel/security/kallsyms/.gitignore @@ -0,0 +1 @@ +kallsyms diff --git a/testcases/kernel/security/kallsyms/Makefile b/testcases/kernel/security/kallsyms/Makefile new file mode 100644 index 00000000..5ea7d67d --- /dev/null +++ b/testcases/kernel/security/kallsyms/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/security/kallsyms/kallsyms.c b/testcases/kernel/security/kallsyms/kallsyms.c new file mode 100644 index 00000000..45b3e18d --- /dev/null +++ b/testcases/kernel/security/kallsyms/kallsyms.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Red Hat, Inc. + */ + +/*\ + * Utilize kernel's symbol table for unauthorized address access. + * + * Access the system symbols with root permission to test whether it's + * possible to read and write the memory addresses of kernel-space + * from user-space. This helps in identifying potential vulnerabilities + * where user-space processes can inappropriately access kernel memory. + * + * Steps: + * + * 1. Start a process that reads all symbols and their addresses from + * /proc/kallsyms and stores them in a linked list. + * + * 2. Attempt to write to each kernel address found in the linked list. + * The expectation is that each attempt will fail with a SIGSEGV + * (segmentation fault), indicating that the user-space process + * cannot write to kernel memory. + * + * 3. Handle each SIGSEGV using a signal handler that sets a flag and + * long jumps out of the faulting context. + * + * 4. If any write operation does not result in a SIGSEGV, log this as + * a potential security vulnerability. + * + * 5. Observe and log the behavior and any system responses to these + * unauthorized access attempts. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_stdio.h" + +struct kallsym { + unsigned long addr; + char type; + char name[128]; +}; + +struct range_struct { + unsigned long start, end; + +}; + +static struct kallsym *sym_table; +static unsigned int nr_symbols; +static sigjmp_buf jmpbuf; +volatile sig_atomic_t segv_caught; +static struct range_struct *ranges; +static int ranges_size, ranges_len; + +static void segv_handler(int sig) +{ + if (sig == SIGSEGV) + segv_caught++; + else + tst_res(TFAIL, "Unexpected signal %s", strsignal(sig)); + + siglongjmp(jmpbuf, 1); +} + +static unsigned int read_kallsyms(struct kallsym *table, unsigned int table_size) +{ + char *line = NULL; + size_t len = 0; + unsigned int nr_syms = 0; + FILE *stream = SAFE_FOPEN("/proc/kallsyms", "r"); + + while (getline(&line, &len, stream) != -1) { + + if (table && nr_syms < table_size) { + sscanf(line, "%lx %c %s", + &table[nr_syms].addr, + &table[nr_syms].type, + table[nr_syms].name); + } + + nr_syms++; + } + + SAFE_FCLOSE(stream); + + return nr_syms; +} + +static void read_proc_self_maps(void) +{ + FILE *fp; + + ranges_len = 0; + fp = fopen("/proc/self/maps", "r"); + if (fp == NULL) + tst_brk(TBROK | TERRNO, "Failed to open /proc/self/maps."); + + while (!feof(fp)) { + unsigned long start, end; + int ret; + + ret = fscanf(fp, "%lx-%lx %*[^\n]\n", &start, &end); + if (ret != 2) { + fclose(fp); + tst_brk(TBROK | TERRNO, "Couldn't parse /proc/self/maps line."); + } + + if (ranges_size < ranges_len + 1) { + ranges_size += 128; + ranges = SAFE_REALLOC(ranges, + ranges_size*sizeof(struct range_struct)); + } + ranges[ranges_len].start = start; + ranges[ranges_len].end = end; + ranges_len++; + } + + fclose(fp); +} + +static int is_address_mapped(unsigned long addr) +{ + int i; + + for (i = 0; i < ranges_len; i++) { + if (ranges[i].start <= addr && addr < ranges[i].end) + return 1; + } + return 0; +} + +static void setup(void) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = segv_handler; + sigaction(SIGSEGV, &sa, NULL); + + nr_symbols = read_kallsyms(NULL, 0); + sym_table = SAFE_CALLOC(nr_symbols, sizeof(*sym_table)); + unsigned int read_symbols = read_kallsyms(sym_table, nr_symbols); + + if (nr_symbols != read_symbols) + tst_res(TWARN, "/proc/kallsyms changed size!?"); +} + +static void access_ksymbols_address(struct kallsym *table) +{ + tst_res(TDEBUG, "Access kernel addr: 0x%lx (%c) (%s)", + table->addr, table->type, table->name); + + if (sigsetjmp(jmpbuf, 1) == 0) { + *(volatile unsigned long *)table->addr = 0; + + tst_res(TFAIL, "Successfully accessed kernel addr 0x%lx (%c) (%s)", + table->addr, table->type, table->name); + } +} + + +static void test_access_kernel_address(void) +{ + int skipped = 0; + + segv_caught = 0; + read_proc_self_maps(); + + for (unsigned int i = 0; i < nr_symbols; i++) { + if (is_address_mapped(sym_table[i].addr)) { + tst_res(TDEBUG, "Skipping userspace mapped address 0x%lx", + sym_table[i].addr); + skipped++; + continue; + } + access_ksymbols_address(&sym_table[i]); + } + + if (segv_caught == (sig_atomic_t)nr_symbols - skipped) + tst_res(TPASS, "Caught %d SIGSEGV in access ksymbols addr, skipped %d", + segv_caught, skipped); + else + tst_res(TFAIL, "Caught %d SIGSEGV but expected %d, skipped %d", + segv_caught, nr_symbols-skipped, skipped); +} + +static void cleanup(void) +{ + if (sym_table) + free(sym_table); +} + +static struct tst_test test = { + .needs_root = 1, + .setup = setup, + .cleanup = cleanup, + .timeout = 60, + .needs_kconfigs = (const char *const[]){ + "CONFIG_KALLSYMS=y", + NULL + }, + .test_all = test_access_kernel_address, +}; diff --git a/testcases/kernel/sound/snd_seq01.c b/testcases/kernel/sound/snd_seq01.c index 31038b93..8231e185 100755 --- a/testcases/kernel/sound/snd_seq01.c +++ b/testcases/kernel/sound/snd_seq01.c @@ -123,7 +123,8 @@ static struct tst_test test = { .tcnt = ARRAY_SIZE(testfunc_list), .setup = setup, .cleanup = cleanup, - .max_runtime = 60, + .runtime = 60, + .min_runtime = 2, .taint_check = TST_TAINT_W | TST_TAINT_D, .tags = (const struct tst_tag[]) { {"linux-git", "d15d662e89fc"}, diff --git a/testcases/kernel/sound/snd_timer01.c b/testcases/kernel/sound/snd_timer01.c index 310169b0..53d1ec91 100755 --- a/testcases/kernel/sound/snd_timer01.c +++ b/testcases/kernel/sound/snd_timer01.c @@ -136,7 +136,8 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .taint_check = TST_TAINT_W | TST_TAINT_D, - .max_runtime = 150, + .runtime = 150, + .min_runtime = 2, .tags = (const struct tst_tag[]) { {"linux-git", "d11662f4f798"}, {"linux-git", "ba3021b2c79b"}, diff --git a/testcases/kernel/syscalls/Makefile b/testcases/kernel/syscalls/Makefile index c6dc8d9e..b2e1b1ca 100755 --- a/testcases/kernel/syscalls/Makefile +++ b/testcases/kernel/syscalls/Makefile @@ -10,11 +10,6 @@ top_srcdir ?= ../../.. include $(top_srcdir)/include/mk/env_pre.mk -ifeq ($(UCLINUX),1) -FILTER_OUT_DIRS += capget capset chmod chown clone fork getcontext llseek \ - mincore mprotect nftw profil remap_file_pages sbrk -endif - ifeq ($(UCLIBC),1) FILTER_OUT_DIRS += profil endif diff --git a/testcases/kernel/syscalls/abort/abort01.c b/testcases/kernel/syscalls/abort/abort01.c index aa9ca3a2..05a92204 100755 --- a/testcases/kernel/syscalls/abort/abort01.c +++ b/testcases/kernel/syscalls/abort/abort01.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Checks that process which called abort() gets killed by SIGIOT and dumps core. * * [Algorithm] diff --git a/testcases/kernel/syscalls/accept/.gitignore b/testcases/kernel/syscalls/accept/.gitignore index 5b146269..f81d4bec 100755 --- a/testcases/kernel/syscalls/accept/.gitignore +++ b/testcases/kernel/syscalls/accept/.gitignore @@ -1,2 +1,3 @@ /accept01 /accept02 +/accept03 diff --git a/testcases/kernel/syscalls/accept/accept01.c b/testcases/kernel/syscalls/accept/accept01.c index 85af0f8a..943af842 100755 --- a/testcases/kernel/syscalls/accept/accept01.c +++ b/testcases/kernel/syscalls/accept/accept01.c @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Verify that accept() returns the proper errno for various failure cases. */ @@ -26,7 +25,6 @@ struct sockaddr_in sin0, sin1, fsin1; int invalid_socketfd = 400; /* anything that is not an open file */ -int devnull_fd; int socket_fd; int udp_fd; @@ -45,10 +43,6 @@ static struct test_case { (struct sockaddr *)&fsin1, sizeof(fsin1), EBADF, "bad file descriptor" }, - { - PF_INET, SOCK_STREAM, 0, &devnull_fd, (struct sockaddr *)&fsin1, - sizeof(fsin1), ENOTSOCK, "fd is not socket" - }, { PF_INET, SOCK_STREAM, 0, &socket_fd, (struct sockaddr *)3, sizeof(fsin1), EINVAL, "invalid socket buffer" @@ -73,8 +67,6 @@ static void test_setup(void) sin0.sin_port = 0; sin0.sin_addr.s_addr = INADDR_ANY; - devnull_fd = SAFE_OPEN("/dev/null", O_WRONLY); - socket_fd = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); SAFE_BIND(socket_fd, (struct sockaddr *)&sin0, sizeof(sin0)); @@ -88,7 +80,6 @@ static void test_setup(void) static void test_cleanup(void) { - SAFE_CLOSE(devnull_fd); SAFE_CLOSE(socket_fd); SAFE_CLOSE(udp_fd); } diff --git a/testcases/kernel/syscalls/accept/accept02.c b/testcases/kernel/syscalls/accept/accept02.c index 7cb3d697..e50d2e78 100755 --- a/testcases/kernel/syscalls/accept/accept02.c +++ b/testcases/kernel/syscalls/accept/accept02.c @@ -4,8 +4,6 @@ * Author: Christian Amann */ /*\ - * [Description] - * * Test for CVE-2017-8890 * * In Kernels up to 4.10.15 missing commit 657831ff the multicast diff --git a/testcases/kernel/syscalls/accept/accept03.c b/testcases/kernel/syscalls/accept/accept03.c new file mode 100644 index 00000000..bc472397 --- /dev/null +++ b/testcases/kernel/syscalls/accept/accept03.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2023-2024 Cyril Hrubis + */ + +/*\ + * Verify that accept() returns ENOTSOCK or EBADF for non-socket file + * descriptors. The EBADF is returned in the case that the file descriptor has + * not a file associated with it, which is for example in the case of O_PATH + * opened file. + */ + +#include +#include + +#include "tst_test.h" + +void check_accept(struct tst_fd *fd) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = {.s_addr = INADDR_ANY}, + }; + + socklen_t size = sizeof(addr); + + int exp_errno = ENOTSOCK; + + switch (fd->type) { + case TST_FD_UNIX_SOCK: + case TST_FD_INET_SOCK: + return; + /* + * With these two we fail even before we get to the do_accept() because + * the fd does not have a struct file associated. + */ + case TST_FD_OPEN_TREE: + case TST_FD_PATH: + exp_errno = EBADF; + default: + break; + } + + TST_EXP_FAIL2(accept(fd->fd, (void*)&addr, &size), + exp_errno, "accept() on %s", tst_fd_desc(fd)); +} + +static void verify_accept(void) +{ + TST_FD_FOREACH(fd) + check_accept(&fd); +} + +static struct tst_test test = { + .test_all = verify_accept, +}; diff --git a/testcases/kernel/syscalls/access/Makefile b/testcases/kernel/syscalls/access/Makefile index 5d89a6c0..044619fb 100755 --- a/testcases/kernel/syscalls/access/Makefile +++ b/testcases/kernel/syscalls/access/Makefile @@ -3,10 +3,6 @@ top_srcdir ?= ../../../.. -ifeq ($(UCLINUX),1) -FILTER_OUT_MAKE_TARGETS += access02 access03 -endif - include $(top_srcdir)/include/mk/testcases.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/access/access01.c b/testcases/kernel/syscalls/access/access01.c index 391c8d44..1da949b5 100755 --- a/testcases/kernel/syscalls/access/access01.c +++ b/testcases/kernel/syscalls/access/access01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for access(2) using F_OK, R_OK, W_OK and X_OK */ @@ -314,6 +312,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 1, .needs_tmpdir = 1, .needs_root = 1, .forks_child = 1, diff --git a/testcases/kernel/syscalls/access/access02.c b/testcases/kernel/syscalls/access/access02.c index c8fe0d0d..67d7b70e 100755 --- a/testcases/kernel/syscalls/access/access02.c +++ b/testcases/kernel/syscalls/access/access02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test access(2) syscall * * - check the existence or read/write/execute permissions on a file (mode argument: F_OK/R_OK/W_OK/X_OK) diff --git a/testcases/kernel/syscalls/access/access03.c b/testcases/kernel/syscalls/access/access03.c index 7cae1ec2..c42c0624 100755 --- a/testcases/kernel/syscalls/access/access03.c +++ b/testcases/kernel/syscalls/access/access03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * access(2) test for errno(s) EFAULT as root and nobody respectively. */ diff --git a/testcases/kernel/syscalls/access/access04.c b/testcases/kernel/syscalls/access/access04.c index b5764a5d..8a9ac5c3 100755 --- a/testcases/kernel/syscalls/access/access04.c +++ b/testcases/kernel/syscalls/access/access04.c @@ -9,8 +9,6 @@ */ /*\ - * [Description] - * * - access() fails with -1 return value and sets errno to EINVAL * if the specified access mode argument is invalid. * - access() fails with -1 return value and sets errno to ENOENT diff --git a/testcases/kernel/syscalls/acct/acct01.c b/testcases/kernel/syscalls/acct/acct01.c index 52c4d41d..6249f6e2 100755 --- a/testcases/kernel/syscalls/acct/acct01.c +++ b/testcases/kernel/syscalls/acct/acct01.c @@ -1,14 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) Linux Test Project, 2003-2023 + * 12/03/2002 Port to LTP robbiew@us.ibm.com + * 06/30/2001 Port to Linux nsharoff@us.ibm.com */ -/* 12/03/2002 Port to LTP robbiew@us.ibm.com */ -/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ - /*\ - * [DOCUMENTATION] * Verify that acct() returns proper errno on failure. */ @@ -24,8 +23,7 @@ #include "tst_test.h" -#define DIR_MODE (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP| \ - S_IXGRP|S_IROTH|S_IXOTH) +#define DIR_MODE 0755 #define FILE_EISDIR "." #define FILE_EACCESS "/dev/null" #define FILE_ENOENT "/tmp/does/not/exist" @@ -33,6 +31,7 @@ #define FILE_TMPFILE "./tmpfile" #define FILE_ELOOP "test_file_eloop1" #define FILE_EROFS "ro_mntpoint/file" +#define FILE_EFAULT "invalid/file/name" static struct passwd *ltpuser; @@ -45,6 +44,7 @@ static char *file_eloop; static char *file_enametoolong; static char *file_erofs; static char *file_null; +static char *file_efault; static void setup_euid(void) { @@ -56,12 +56,22 @@ static void cleanup_euid(void) SAFE_SETEUID(0); } +static void setup_emem(void) +{ + file_efault = SAFE_MMAP(NULL, 1, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); +} +static void cleanup_emem(void) +{ + SAFE_MUNMAP(file_efault, 1); +} + static struct test_case { char **filename; char *desc; int exp_errno; - void (*setupfunc) (); - void (*cleanfunc) (); + void (*setupfunc)(); + void (*cleanfunc)(); } tcases[] = { {&file_eisdir, FILE_EISDIR, EISDIR, NULL, NULL}, {&file_eaccess, FILE_EACCESS, EACCES, NULL, NULL}, @@ -72,6 +82,7 @@ static struct test_case { {&file_eloop, FILE_ELOOP, ELOOP, NULL, NULL}, {&file_enametoolong, "aaaa...", ENAMETOOLONG, NULL, NULL}, {&file_erofs, FILE_EROFS, EROFS, NULL, NULL}, + {&file_efault, "Invalid address", EFAULT, setup_emem, cleanup_emem}, }; static void setup(void) @@ -79,8 +90,6 @@ static void setup(void) int fd; TEST(acct(NULL)); - if (TST_RET == -1 && TST_ERR == ENOSYS) - tst_brk(TCONF, "acct() system call isn't configured in kernel"); ltpuser = SAFE_GETPWNAM("nobody"); @@ -112,7 +121,7 @@ static void verify_acct(unsigned int nr) tcase->setupfunc(); TST_EXP_FAIL(acct(*tcase->filename), tcase->exp_errno, - "acct(%s)", tcase->desc); + "acct(%s)", tcase->desc); if (tcase->cleanfunc) tcase->cleanfunc(); @@ -135,5 +144,9 @@ static struct tst_test test = { {&file_enametoolong, .size = PATH_MAX+2}, {&file_erofs, .str = FILE_EROFS}, {} + }, + .needs_kconfigs = (const char *[]) { + "CONFIG_BSD_PROCESS_ACCT=y", + NULL, } }; diff --git a/testcases/kernel/syscalls/acct/acct02.c b/testcases/kernel/syscalls/acct/acct02.c index b8eb1aad..90555db9 100755 --- a/testcases/kernel/syscalls/acct/acct02.c +++ b/testcases/kernel/syscalls/acct/acct02.c @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) SUSE LLC, 2019 - * Author: Christian Amann + * Copyright (c) SUSE LLC, 2019 + * Copyright (c) Linux Test Project, 2019-2023 + * Author: Christian Amann */ /*\ - * [DOCUMENTATION] - * * This tests if the kernel writes correct data to the * process accounting file. * @@ -19,8 +18,8 @@ * file, the contents get parsed until the correct entry is found, or EOF * is reached. * - * This is also accidental regression test for: - * 4d9570158b626 kernel/acct.c: fix the acct->needcheck check in check_free_space() + * This is also regression test for commit: + * 4d9570158b62 ("kernel/acct.c: fix the acct->needcheck check in check_free_space()") */ #include @@ -55,10 +54,7 @@ static union acct_union { static int acct_version_is_3(void) { - struct tst_kconfig_var kconfig = { - .id = ACCT_V3, - .id_len = sizeof(ACCT_V3)-1, - }; + struct tst_kconfig_var kconfig = TST_KCONFIG_INIT(ACCT_V3); tst_kconfig_read(&kconfig, 1); @@ -188,7 +184,7 @@ static void run(void) if (read_bytes != acct_size) { tst_res(TFAIL, "incomplete read %i bytes, expected %i", - read_bytes, acct_size); + read_bytes, acct_size); goto exit; } diff --git a/testcases/kernel/syscalls/acct/acct02_helper.c b/testcases/kernel/syscalls/acct/acct02_helper.c index 66017cb9..0f55a6ae 100755 --- a/testcases/kernel/syscalls/acct/acct02_helper.c +++ b/testcases/kernel/syscalls/acct/acct02_helper.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) SUSE LLC, 2019 - * Author: Christian Amann + * Copyright (c) SUSE LLC, 2019 + * Author: Christian Amann */ /* * Dummy program used in acct02 diff --git a/testcases/kernel/syscalls/add_key/add_key01.c b/testcases/kernel/syscalls/add_key/add_key01.c index 1983e253..d46d57e3 100755 --- a/testcases/kernel/syscalls/add_key/add_key01.c +++ b/testcases/kernel/syscalls/add_key/add_key01.c @@ -23,7 +23,7 @@ static char *logon_buf, *logon_buf1; static char *big_key_buf, *big_key_buf1; static unsigned int logon_nsup, big_key_nsup; -struct tcase { +static struct tcase { const char *type; const char *desc; char **buf; diff --git a/testcases/kernel/syscalls/add_key/add_key02.c b/testcases/kernel/syscalls/add_key/add_key02.c index 98dd5b90..4d5a4ef7 100755 --- a/testcases/kernel/syscalls/add_key/add_key02.c +++ b/testcases/kernel/syscalls/add_key/add_key02.c @@ -23,7 +23,7 @@ #include "tst_test.h" #include "lapi/keyctl.h" -struct tcase { +static struct tcase { const char *type; size_t plen; } tcases[] = { diff --git a/testcases/kernel/syscalls/add_key/add_key05.c b/testcases/kernel/syscalls/add_key/add_key05.c index 74b0b54d..c9a2f840 100755 --- a/testcases/kernel/syscalls/add_key/add_key05.c +++ b/testcases/kernel/syscalls/add_key/add_key05.c @@ -144,7 +144,7 @@ static void verify_max_bytes(void) static void verify_max_keys(void) { int i, used_key, max_key; - char desc[10]; + char desc[15]; tst_res(TINFO, "test max keys under unprivileged user"); parse_proc_key_users(&used_key, &max_key, NULL, NULL); @@ -203,8 +203,6 @@ static void do_test(unsigned int n) tst_reap_children(); useri++; - - return; } static void cleanup(void) @@ -214,6 +212,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 2, .test = do_test, .tcnt = 2, .needs_root = 1, diff --git a/testcases/kernel/syscalls/adjtimex/adjtimex01.c b/testcases/kernel/syscalls/adjtimex/adjtimex01.c index 60b3544a..a03ecf2c 100755 --- a/testcases/kernel/syscalls/adjtimex/adjtimex01.c +++ b/testcases/kernel/syscalls/adjtimex/adjtimex01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. diff --git a/testcases/kernel/syscalls/adjtimex/adjtimex02.c b/testcases/kernel/syscalls/adjtimex/adjtimex02.c index e66ba2a5..836d49fa 100755 --- a/testcases/kernel/syscalls/adjtimex/adjtimex02.c +++ b/testcases/kernel/syscalls/adjtimex/adjtimex02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests for adjtimex() error conditions: * * - EPERM with SET_MODE as nobody diff --git a/testcases/kernel/syscalls/adjtimex/adjtimex03.c b/testcases/kernel/syscalls/adjtimex/adjtimex03.c index 7056973c..5521e4a3 100755 --- a/testcases/kernel/syscalls/adjtimex/adjtimex03.c +++ b/testcases/kernel/syscalls/adjtimex/adjtimex03.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * CVE-2018-11508: Test 4-byte kernel data leak via adjtimex. * * On calling the adjtimex() function call with invalid mode (let's say @@ -23,7 +21,7 @@ * To test that, Pass struct timex buffer filled with zero with * some INVALID mode to the system call adjtimex. Passing an invalid * parameters will not call do_adjtimex() and before that, it shall throw - * an error(On error test shall not break). Therefore, none of the parameters + * an error (on error test shall not break). Therefore, none of the parameters * will get initialized. * * On reading the last attribute tai of the struct, if the attribute is non- diff --git a/testcases/kernel/syscalls/alarm/alarm02.c b/testcases/kernel/syscalls/alarm/alarm02.c index fcf70c5f..4f012bb2 100755 --- a/testcases/kernel/syscalls/alarm/alarm02.c +++ b/testcases/kernel/syscalls/alarm/alarm02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that alarm() returns: * * - zero when there was no previously scheduled alarm diff --git a/testcases/kernel/syscalls/alarm/alarm03.c b/testcases/kernel/syscalls/alarm/alarm03.c index b010e754..7c98db07 100755 --- a/testcases/kernel/syscalls/alarm/alarm03.c +++ b/testcases/kernel/syscalls/alarm/alarm03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that alarms created by alarm() are not inherited by children * created via fork. */ diff --git a/testcases/kernel/syscalls/alarm/alarm05.c b/testcases/kernel/syscalls/alarm/alarm05.c index 2eeb1c22..f8f62691 100755 --- a/testcases/kernel/syscalls/alarm/alarm05.c +++ b/testcases/kernel/syscalls/alarm/alarm05.c @@ -9,8 +9,6 @@ */ /*\ - * [Description] - * * The return value of the alarm system call should be equal to the * amount previously remaining in the alarm clock. * A SIGALRM signal should be received after the specified amount of @@ -44,6 +42,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 2, .test_all = run, .setup = setup, }; diff --git a/testcases/kernel/syscalls/alarm/alarm06.c b/testcases/kernel/syscalls/alarm/alarm06.c index 82c0d44b..76f4226f 100755 --- a/testcases/kernel/syscalls/alarm/alarm06.c +++ b/testcases/kernel/syscalls/alarm/alarm06.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that any pending alarm() is canceled when seconds is zero. */ @@ -41,6 +39,7 @@ static void verify_alarm(void) } static struct tst_test test = { + .timeout = 4, .setup = setup, .test_all = verify_alarm, }; diff --git a/testcases/kernel/syscalls/alarm/alarm07.c b/testcases/kernel/syscalls/alarm/alarm07.c index 64aed507..8a1e500d 100755 --- a/testcases/kernel/syscalls/alarm/alarm07.c +++ b/testcases/kernel/syscalls/alarm/alarm07.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that SIGALRM signal scheduled by alarm() in the parent process * is not delivered to the child process. */ @@ -47,6 +45,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 4, .test_all = verify_alarm, .setup = setup, .forks_child = 1, diff --git a/testcases/kernel/syscalls/arch_prctl/.gitignore b/testcases/kernel/syscalls/arch_prctl/.gitignore new file mode 100644 index 00000000..24871e24 --- /dev/null +++ b/testcases/kernel/syscalls/arch_prctl/.gitignore @@ -0,0 +1 @@ +/arch_prctl01 diff --git a/testcases/kernel/syscalls/arch_prctl/Makefile b/testcases/kernel/syscalls/arch_prctl/Makefile new file mode 100644 index 00000000..272949d5 --- /dev/null +++ b/testcases/kernel/syscalls/arch_prctl/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) UnionTech Software Technology Co.,Ltd. 2024 + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/arch_prctl/arch_prctl01.c b/testcases/kernel/syscalls/arch_prctl/arch_prctl01.c new file mode 100644 index 00000000..8ce905c6 --- /dev/null +++ b/testcases/kernel/syscalls/arch_prctl/arch_prctl01.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) UnionTech Software Technology Co.,Ltd., 2024 + * Author: Lu Fei + */ + +/*\ + * Simple test on arch_prctl to set and get cpuid instruction of test thread. + */ + +#include "tst_test.h" +#include "tst_safe_stdio.h" +#include "lapi/syscalls.h" +#include "lapi/arch_prctl.h" +#include +#include + +static int arch_prctl_get(int code) +{ + return tst_syscall(__NR_arch_prctl, code, NULL); +} + +static int arch_prctl_set(int code, unsigned long addr) +{ + return tst_syscall(__NR_arch_prctl, code, addr); +} + +static bool tag; + +static void setup(void) +{ + FILE *f; + char flag_mid[] = " cpuid_fault "; + char flag_end[] = " cpuid_fault\n"; + char *line = NULL; + size_t len = 0; + + tag = false; + + f = SAFE_FOPEN("/proc/cpuinfo", "r"); + + while (getline(&line, &len, f) != -1) + if (strncmp(line, "flags", strlen("flags")) == 0 && + (strstr(line, flag_mid) != NULL || + strstr(line, flag_end) != NULL)) { + tag = true; + break; + } +} + +static void run(unsigned int index) +{ + if (tag) + TST_EXP_PASS(arch_prctl_set(ARCH_SET_CPUID, index)); + else + TST_EXP_FAIL(arch_prctl_set(ARCH_SET_CPUID, index), ENODEV); + + // if cpu has cpuid_fault flag, ARCH_GET_CPUID returns what has been + // set: index, otherwise, returns default status: 1 + int exp = tag ? index : 1; + + TEST(arch_prctl_get(ARCH_GET_CPUID)); + if (TST_RET == exp) + tst_res(TPASS, "get cpuid succeed."); + else + tst_res(TFAIL, "get wrong cpuid status"); +} + +static struct tst_test test = { + .test = run, + .setup = setup, + .tcnt = 2, + .min_kver = "4.12", + .supported_archs = (const char *const []){"x86_64", "x86", NULL} +}; diff --git a/testcases/kernel/syscalls/bind/bind01.c b/testcases/kernel/syscalls/bind/bind01.c index c008819a..11825546 100755 --- a/testcases/kernel/syscalls/bind/bind01.c +++ b/testcases/kernel/syscalls/bind/bind01.c @@ -17,11 +17,16 @@ #include "tst_test.h" -int inet_socket; -int dev_null; +#define DIR_ENOTDIR "dir_enotdir" +#define TEST_ENOTDIR "test_enotdir" -struct sockaddr_in sin1, sin2, sin3; -struct sockaddr_un sun; +static int inet_socket; +static int dev_null; +static int fd_ebadf = -1; +static int fd_enotdir; + +static struct sockaddr_in sin1, sin2, sin3; +static struct sockaddr_un sun, sock_enotdir; static struct test_case { int *socket_fd; @@ -41,24 +46,28 @@ static struct test_case { EAFNOSUPPORT, "UNIX-domain of current directory" }, { &inet_socket, (struct sockaddr *)&sin3, sizeof(sin3), -1, EADDRNOTAVAIL, "non-local address" }, + { &fd_ebadf, (struct sockaddr *)&sin1, sizeof(sin1), -1, + EBADF, "sockfd is not a valid file descriptor" }, + { &fd_enotdir, (struct sockaddr *)&sock_enotdir, sizeof(sock_enotdir), -1, + ENOTDIR, "a component of addr prefix is not a directory"}, }; -void verify_bind(unsigned int nr) +static void verify_bind(unsigned int nr) { struct test_case *tcase = &tcases[nr]; if (tcase->experrno) { TST_EXP_FAIL(bind(*tcase->socket_fd, tcase->sockaddr, tcase->salen), - tcase->experrno, "%s", tcase->desc); + tcase->experrno, "%s", tcase->desc); } else { TST_EXP_PASS(bind(*tcase->socket_fd, tcase->sockaddr, tcase->salen), - "%s", tcase->desc); + "%s", tcase->desc); SAFE_CLOSE(inet_socket); inet_socket = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); } } -void test_setup(void) +static void test_setup(void) { /* initialize sockaddr's */ sin1.sin_family = AF_INET; @@ -78,14 +87,26 @@ void test_setup(void) sun.sun_family = AF_UNIX; strncpy(sun.sun_path, ".", sizeof(sun.sun_path)); + SAFE_TOUCH(DIR_ENOTDIR, 0777, NULL); + sock_enotdir.sun_family = AF_UNIX; + strncpy(sock_enotdir.sun_path, DIR_ENOTDIR "/" TEST_ENOTDIR, + sizeof(sock_enotdir.sun_path)); + inet_socket = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); dev_null = SAFE_OPEN("/dev/null", O_WRONLY); + fd_enotdir = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); } -void test_cleanup(void) +static void test_cleanup(void) { - SAFE_CLOSE(inet_socket); - SAFE_CLOSE(dev_null); + if (inet_socket > 0) + SAFE_CLOSE(inet_socket); + + if (dev_null > 0) + SAFE_CLOSE(dev_null); + + if (fd_enotdir > 0) + SAFE_CLOSE(fd_enotdir); } static struct tst_test test = { @@ -93,4 +114,5 @@ static struct tst_test test = { .setup = test_setup, .cleanup = test_cleanup, .test = verify_bind, + .needs_tmpdir = 1, }; diff --git a/testcases/kernel/syscalls/bind/bind02.c b/testcases/kernel/syscalls/bind/bind02.c index a997157d..73ebd4aa 100755 --- a/testcases/kernel/syscalls/bind/bind02.c +++ b/testcases/kernel/syscalls/bind/bind02.c @@ -6,9 +6,8 @@ * Copyright (c) 2019 Martin Doucha */ -/* - * Test Description: - * Make sure bind() of privileged port gives EACCESS error for non-root users. +/*\ + * Make sure bind() of privileged port gives EACCESS error for non-root users. */ #include @@ -37,7 +36,7 @@ static void run(void) servaddr.sin_port = htons(TCP_PRIVILEGED_PORT); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); TST_EXP_FAIL(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)), - EACCES, "bind()"); + EACCES, "bind()"); SAFE_CLOSE(sockfd); } diff --git a/testcases/kernel/syscalls/bind/bind03.c b/testcases/kernel/syscalls/bind/bind03.c index 8c95cd79..3b1fd9af 100755 --- a/testcases/kernel/syscalls/bind/bind03.c +++ b/testcases/kernel/syscalls/bind/bind03.c @@ -29,14 +29,14 @@ static void run(void) * rebound. */ TST_EXP_FAIL(bind(sock1, (struct sockaddr *)&sun2, sizeof(sun2)), - EINVAL, "re-bind() socket"); + EINVAL, "re-bind() socket"); /* * Since a socket is already bound to the pathname, it can't be bound * to a second socket. Expected error is EADDRINUSE. */ TST_EXP_FAIL(bind(sock2, (struct sockaddr *)&sun1, sizeof(sun1)), - EADDRINUSE, "bind() with bound pathname"); + EADDRINUSE, "bind() with bound pathname"); /* * Kernel is buggy since it creates the node in fileystem first, then @@ -74,8 +74,11 @@ static void setup(void) static void cleanup(void) { - SAFE_CLOSE(sock1); - SAFE_CLOSE(sock2); + if (sock1 > 0) + SAFE_CLOSE(sock1); + + if (sock2 > 0) + SAFE_CLOSE(sock2); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/bind/bind04.c b/testcases/kernel/syscalls/bind/bind04.c index d8456e73..2a46559d 100755 --- a/testcases/kernel/syscalls/bind/bind04.c +++ b/testcases/kernel/syscalls/bind/bind04.c @@ -161,6 +161,7 @@ static void test_bind(unsigned int n) } static struct tst_test test = { + .timeout = 1, .test = test_bind, .tcnt = ARRAY_SIZE(testcase_list), .needs_tmpdir = 1, diff --git a/testcases/kernel/syscalls/bind/bind06.c b/testcases/kernel/syscalls/bind/bind06.c index 7c3300c4..988574a8 100755 --- a/testcases/kernel/syscalls/bind/bind06.c +++ b/testcases/kernel/syscalls/bind/bind06.c @@ -92,7 +92,7 @@ static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 300, + .min_runtime = 300, .taint_check = TST_TAINT_W | TST_TAINT_D, .needs_kconfigs = (const char *[]) { "CONFIG_USER_NS=y", diff --git a/testcases/kernel/syscalls/bind/libbind.h b/testcases/kernel/syscalls/bind/libbind.h index 58f1bb7e..aeb1ab3c 100755 --- a/testcases/kernel/syscalls/bind/libbind.h +++ b/testcases/kernel/syscalls/bind/libbind.h @@ -1,6 +1,5 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2019 Martin Doucha +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2019 Martin Doucha */ /* diff --git a/testcases/kernel/syscalls/bpf/bpf_common.c b/testcases/kernel/syscalls/bpf/bpf_common.c index 95b5bc12..9148b043 100755 --- a/testcases/kernel/syscalls/bpf/bpf_common.c +++ b/testcases/kernel/syscalls/bpf/bpf_common.c @@ -49,7 +49,10 @@ int bpf_map_create(union bpf_attr *const attr) int bpf_map_array_create(const uint32_t max_entries) { - union bpf_attr map_attr = { + /* zero-initialize entire struct including padding bits */ + union bpf_attr map_attr = {}; + + map_attr = (union bpf_attr) { .map_type = BPF_MAP_TYPE_ARRAY, .key_size = 4, .value_size = 8, @@ -64,13 +67,18 @@ void bpf_map_array_get(const int map_fd, const uint32_t *const array_indx, uint64_t *const array_val) { - union bpf_attr elem_attr = { + /* zero-initialize entire struct including padding bits */ + union bpf_attr elem_attr = {}; + int ret; + + elem_attr = (union bpf_attr) { .map_fd = map_fd, .key = ptr_to_u64(array_indx), .value = ptr_to_u64(array_val), .flags = 0 }; - const int ret = bpf(BPF_MAP_LOOKUP_ELEM, &elem_attr, sizeof(elem_attr)); + + ret = bpf(BPF_MAP_LOOKUP_ELEM, &elem_attr, sizeof(elem_attr)); if (ret) { tst_brk(TBROK | TTERRNO, diff --git a/testcases/kernel/syscalls/bpf/bpf_map01.c b/testcases/kernel/syscalls/bpf/bpf_map01.c index 94f9b787..7fcf8e00 100755 --- a/testcases/kernel/syscalls/bpf/bpf_map01.c +++ b/testcases/kernel/syscalls/bpf/bpf_map01.c @@ -1,13 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 Richard Palethorpe - * + */ + +/*\ * Trivial Extended Berkeley Packet Filter (eBPF) test. * * Sanity check creating and updating maps. */ -/* - * If test is executed in a loop and limit for locked memory (ulimit -l) is + + /* + * If the test is executed in a loop and limit for locked memory (ulimit -l) is * too low bpf() call can fail with EPERM due to deffered freeing. */ @@ -142,7 +145,6 @@ static struct tst_test test = { .tcnt = ARRAY_SIZE(map_types), .test = run, .setup = setup, - .min_kver = "3.19", .bufs = (struct tst_buffers []) { {&key4, .size = 4}, {&key8, .size = 8}, diff --git a/testcases/kernel/syscalls/bpf/bpf_prog01.c b/testcases/kernel/syscalls/bpf/bpf_prog01.c index de4f68ce..44ee8c1c 100755 --- a/testcases/kernel/syscalls/bpf/bpf_prog01.c +++ b/testcases/kernel/syscalls/bpf/bpf_prog01.c @@ -1,21 +1,25 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 Richard Palethorpe - * + */ + +/*\ * Trivial Extended Berkeley Packet Filter (eBPF) test. * * Sanity check loading and running bytecode. * - * Test flow: - * 1. Create array map - * 2. Load eBPF program - * 3. Attach program to socket - * 4. Send packet on socket - * 5. This should trigger eBPF program which writes to array map - * 6. Verify array map was written to + * [Algorithm] + * + * - Create array map + * - Load eBPF program + * - Attach program to socket + * - Send packet on socket + * - This should trigger eBPF program which writes to array map + * - Verify array map was written to */ + /* - * If test is executed in a loop and limit for locked memory (ulimit -l) is + * If the test is executed in a loop and limit for locked memory (ulimit -l) is * too low bpf() call can fail with EPERM due to deffered freeing. */ @@ -101,7 +105,6 @@ void run(void) static struct tst_test test = { .setup = setup, .test_all = run, - .min_kver = "3.19", .bufs = (struct tst_buffers []) { {&log, .size = BUFSIZ}, {&attr, .size = sizeof(*attr)}, diff --git a/testcases/kernel/syscalls/bpf/bpf_prog02.c b/testcases/kernel/syscalls/bpf/bpf_prog02.c index fd3e535c..8bc7996f 100755 --- a/testcases/kernel/syscalls/bpf/bpf_prog02.c +++ b/testcases/kernel/syscalls/bpf/bpf_prog02.c @@ -1,13 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 Richard Palethorpe - * + */ + +/*\ * Check if eBPF can do arithmetic with 64bits. This targets a specific * regression which only effects unprivileged users who are subject to extra * pointer arithmetic checks during verification. * - * Fixed by commit 3612af783cf52c74a031a2f11b82247b2599d3cd. - * https://new.blog.cloudflare.com/ebpf-cant-count/ + * Fixed by kernel commit + * 3612af783cf5 ("bpf: fix sanitation rewrite in case of non-pointers") + * + * https://blog.cloudflare.com/ebpf-cant-count/ * * This test is very similar in structure to bpf_prog01 which is better * annotated. @@ -109,7 +113,6 @@ static void run(void) static struct tst_test test = { .setup = setup, .test_all = run, - .min_kver = "3.18", .caps = (struct tst_cap []) { TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN), {} diff --git a/testcases/kernel/syscalls/bpf/bpf_prog03.c b/testcases/kernel/syscalls/bpf/bpf_prog03.c index 35bb841c..f37560c3 100755 --- a/testcases/kernel/syscalls/bpf/bpf_prog03.c +++ b/testcases/kernel/syscalls/bpf/bpf_prog03.c @@ -1,19 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 Richard Palethorpe - * Original byte code was provided by jannh@google.com - * - * Check for the bug fixed by 95a762e2c8c942780948091f8f2a4f32fce1ac6f - * "bpf: fix incorrect sign extension in check_alu_op()" + * Original byte code was provided by Jann Horn + */ + +/*\ * CVE-2017-16995 * - * This test is very similar to the reproducer found here: + * Test for the bug fixed by kernel commit + * 95a762e2c8c9 ("bpf: fix incorrect sign extension in check_alu_op()") + * + * The test is very similar to the original reproducer: * https://bugs.chromium.org/p/project-zero/issues/detail?id=1454 * * However it has been modified to try to corrupt the map struct instead of * writing to a noncanonical pointer. This appears to be more reliable at * producing stack traces and confirms we would be able to overwrite the ops - * function pointers, as suggested by Jan. + * function pointers, as suggested by Jan Horn. * * If the eBPF code is loaded then this is considered a failure regardless of * whether it is able to cause any visible damage. @@ -147,7 +150,6 @@ exit: static struct tst_test test = { .setup = setup, .test_all = run, - .min_kver = "3.18", .caps = (struct tst_cap []) { TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN), {} diff --git a/testcases/kernel/syscalls/bpf/bpf_prog04.c b/testcases/kernel/syscalls/bpf/bpf_prog04.c index cf3bb125..35ad8dd5 100755 --- a/testcases/kernel/syscalls/bpf/bpf_prog04.c +++ b/testcases/kernel/syscalls/bpf/bpf_prog04.c @@ -4,19 +4,14 @@ * Copyright (c) 2020 SUSE LLC */ -/* +/*\ * CVE 2018-18445 * * Check that eBPF verifier correctly handles 32-bit arithmetic, in particular * the right bit shift instruction. It is an error if the BPF program passes * verification regardless of whether it then causes any actual damage. Kernel * bug fixed in: - * - * commit b799207e1e1816b09e7a5920fbb2d5fcf6edd681 - * Author: Jann Horn - * Date: Fri Oct 5 18:17:59 2018 +0200 - * - * bpf: 32-bit RSH verification must truncate input before the ALU op + * b799207e1e18 ("bpf: 32-bit RSH verification must truncate input before the ALU op") */ #include @@ -108,7 +103,6 @@ static void run(void) static struct tst_test test = { .setup = setup, .test_all = run, - .min_kver = "3.18", .taint_check = TST_TAINT_W | TST_TAINT_D, .caps = (struct tst_cap []) { TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN), diff --git a/testcases/kernel/syscalls/bpf/bpf_prog05.c b/testcases/kernel/syscalls/bpf/bpf_prog05.c index 742beab0..05cb34e1 100755 --- a/testcases/kernel/syscalls/bpf/bpf_prog05.c +++ b/testcases/kernel/syscalls/bpf/bpf_prog05.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Compare the effects of 32-bit div/mod by zero with the "expected" * behaviour. * @@ -190,9 +188,9 @@ static void run(void) } static struct tst_test test = { + .timeout = 20, .setup = setup, .test_all = run, - .min_kver = "3.18", .taint_check = TST_TAINT_W | TST_TAINT_D, .caps = (struct tst_cap []) { TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN), diff --git a/testcases/kernel/syscalls/bpf/bpf_prog06.c b/testcases/kernel/syscalls/bpf/bpf_prog06.c index cee9616c..f701e959 100644 --- a/testcases/kernel/syscalls/bpf/bpf_prog06.c +++ b/testcases/kernel/syscalls/bpf/bpf_prog06.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * ringbuf_submit takes a pointer to a ringbuf record, but not the * size of this record. The verifier only validates offset ptrs[1] passed * to functions if the function has a size parameter. So we can @@ -131,6 +129,7 @@ static void run(void) } static struct tst_test test = { + .timeout = 20, .setup = setup, .test_all = run, .min_kver = "5.8", diff --git a/testcases/kernel/syscalls/bpf/bpf_prog07.c b/testcases/kernel/syscalls/bpf/bpf_prog07.c index dab5bb8a..e3bf6e7c 100644 --- a/testcases/kernel/syscalls/bpf/bpf_prog07.c +++ b/testcases/kernel/syscalls/bpf/bpf_prog07.c @@ -4,9 +4,7 @@ */ /*\ - * [Description] - * - * The verifier did not properly restrict some *_OR_NULL pointer + * The verifier did not properly restrict some \*_OR_NULL pointer * types. Including RET_PTR_TO_ALLOC_MEM_OR_NULL which is returned by * ringbuf_reserve. Somehow this means they can be used to perform * arbitrary pointer arithmetic. @@ -20,18 +18,14 @@ * eBPF if it can. This will result in an instant crash or memory * corruption which may later cause a crash. * - * This test is adapted from a full reproducer which can be found here: - * https://github.com/tr3ee/CVE-2022-23222 + * This test is adapted from a reproducer from + * https://github.com/tr3ee/CVE-2022-23222. * * It's recommended to disable unprivileged eBPF by setting - * /proc/sys/kernel/unprivileged_bpf_disabled. Also there is a - * specific fix for this issue: + * /proc/sys/kernel/unprivileged_bpf_disabled. * - * commit 64620e0a1e712a778095bd35cbb277dc2259281f - * Author: Daniel Borkmann - * Date: Tue Jan 11 14:43:41 2022 +0000 - * - * bpf: Fix out of bounds access for ringbuf helpers + * Also there is a specific fix for this issue in kernel v5.16 + * 64620e0a1e712 ("bpf: Fix out of bounds access for ringbuf helpers"). */ #include @@ -139,6 +133,7 @@ static void run(void) } static struct tst_test test = { + .timeout = 20, .setup = setup, .test_all = run, .min_kver = "5.8", diff --git a/testcases/kernel/syscalls/brk/brk02.c b/testcases/kernel/syscalls/brk/brk02.c index 64931bc8..201664e8 100755 --- a/testcases/kernel/syscalls/brk/brk02.c +++ b/testcases/kernel/syscalls/brk/brk02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Expand brk() by at least 2 pages to ensure there is a newly created VMA * and not expanding the original due to multiple anon pages. mprotect() that * new VMA then brk() back to the original address therefore causing a munmap of diff --git a/testcases/kernel/syscalls/cachestat/.gitignore b/testcases/kernel/syscalls/cachestat/.gitignore new file mode 100644 index 00000000..a3611a53 --- /dev/null +++ b/testcases/kernel/syscalls/cachestat/.gitignore @@ -0,0 +1,4 @@ +cachestat01 +cachestat02 +cachestat03 +cachestat04 diff --git a/testcases/kernel/syscalls/cachestat/Makefile b/testcases/kernel/syscalls/cachestat/Makefile new file mode 100644 index 00000000..62b00d2f --- /dev/null +++ b/testcases/kernel/syscalls/cachestat/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +LDLIBS += -lrt + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/cachestat/cachestat.h b/testcases/kernel/syscalls/cachestat/cachestat.h new file mode 100644 index 00000000..efce6dc7 --- /dev/null +++ b/testcases/kernel/syscalls/cachestat/cachestat.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef CACHESTAT_H__ +#define CACHESTAT_H__ + +#include "tst_test.h" +#include "lapi/mman.h" + +static inline void print_cachestat(struct cachestat *cs) +{ + tst_res(TDEBUG, + "nr_cache=%lu " + "nr_dirty=%lu " + "nr_writeback=%lu " + "nr_evicted=%lu " + "nr_recently_evicted=%lu", + cs->nr_cache, + cs->nr_dirty, + cs->nr_writeback, + cs->nr_evicted, + cs->nr_recently_evicted); +} + +#endif diff --git a/testcases/kernel/syscalls/cachestat/cachestat01.c b/testcases/kernel/syscalls/cachestat/cachestat01.c new file mode 100644 index 00000000..aa6f21d4 --- /dev/null +++ b/testcases/kernel/syscalls/cachestat/cachestat01.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that cachestat() syscall is properly counting cached pages + * written inside a file. If storage device synchronization is requested, test + * will check if the number of dirty pages is zero. + * + * [Algorithm] + * + * - create a file with specific amount of pages + * - synchronize storage device, if needed + * - monitor file with cachestat() + * - check if the right amount of pages have been moved into cache + * - if storage device synchronization is requested, check that dirty pages is + * zero + */ + +#include +#include "cachestat.h" + +#define MNTPOINT "mntpoint" +#define FILENAME MNTPOINT "/myfile.bin" + +static int page_size, num_shift; +static char *page_data; +static struct cachestat *cs; +static struct cachestat_range *cs_range; + +static void test_cached_pages(const unsigned int use_sync, const int num_pages) +{ + int fd; + + tst_res(TINFO, "%s file synchronization", use_sync ? "Enable" : "Disable"); + tst_res(TINFO, "Number of pages: %d", num_pages); + + memset(cs, 0, sizeof(struct cachestat)); + + fd = SAFE_OPEN(FILENAME, O_RDWR | O_CREAT, 0600); + + for (int i = 0; i < num_pages; i++) + SAFE_WRITE(0, fd, page_data, page_size); + + if (use_sync) + fsync(fd); + + cs_range->off = 0; + cs_range->len = page_size * num_pages; + + memset(cs, 0xff, sizeof(*cs)); + + TST_EXP_PASS(cachestat(fd, cs_range, cs, 0)); + print_cachestat(cs); + + TST_EXP_EQ_LI(cs->nr_cache + cs->nr_evicted, num_pages); + + if (use_sync) + TST_EXP_EQ_LI(cs->nr_dirty, 0); + + SAFE_CLOSE(fd); + SAFE_UNLINK(FILENAME); +} + +static void run(unsigned int use_sync) +{ + for (int i = 0; i < num_shift; i++) + test_cached_pages(use_sync, 1 << i); +} + +static void setup(void) +{ + page_size = (int)sysconf(_SC_PAGESIZE); + + for (num_shift = 0; num_shift <= 15; num_shift++) { + if ((1lu<= tst_device->size) { + tst_res(TINFO, "Limiting num_shift to %i\n", num_shift); + break; + } + } + + page_data = SAFE_MALLOC(page_size); + memset(page_data, 'a', page_size); +} + +static void cleanup(void) +{ + free(page_data); +} + +static struct tst_test test = { + .timeout = 13, + .test = run, + .tcnt = 2, + .setup = setup, + .cleanup = cleanup, + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .skip_filesystems = (const char *const []) { + "fuse", + "tmpfs", + NULL + }, + .bufs = (struct tst_buffers []) { + {&cs, .size = sizeof(struct cachestat)}, + {&cs_range, .size = sizeof(struct cachestat_range)}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/cachestat/cachestat02.c b/testcases/kernel/syscalls/cachestat/cachestat02.c new file mode 100644 index 00000000..72428ee8 --- /dev/null +++ b/testcases/kernel/syscalls/cachestat/cachestat02.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that cachestat() syscall is properly counting cached pages + * written inside a shared memory. + * + * [Algorithm] + * + * - create a shared memory with a specific amount of pages + * - monitor file with cachestat() + * - check if the right amount of pages have been moved into cache + */ + +#include +#include "cachestat.h" + +#define FILENAME "myfile.bin" + +static int page_size; +static char *page_data; +static struct cachestat *cs; +static struct cachestat_range *cs_range; + +static void test_cached_pages(const int num_pages) +{ + int fd, file_size; + + tst_res(TINFO, "Number of pages: %d", num_pages); + + memset(cs, 0, sizeof(struct cachestat)); + + fd = shm_open(FILENAME, O_RDWR | O_CREAT, 0600); + if (fd < 0) + tst_brk(TBROK | TERRNO, "shm_open error"); + + file_size = page_size * num_pages; + + cs_range->off = 0; + cs_range->len = file_size; + + SAFE_FTRUNCATE(fd, file_size); + for (int i = 0; i < num_pages; i++) + SAFE_WRITE(0, fd, page_data, page_size); + + memset(cs, 0xff, sizeof(*cs)); + + TST_EXP_PASS(cachestat(fd, cs_range, cs, 0)); + print_cachestat(cs); + + TST_EXP_EQ_LI(cs->nr_cache + cs->nr_evicted, num_pages); + + SAFE_CLOSE(fd); + shm_unlink(FILENAME); +} + +static void run(void) +{ + for (int i = 0; i < 10; i++) + test_cached_pages(1 << i); +} + +static void setup(void) +{ + page_size = (int)sysconf(_SC_PAGESIZE); + + page_data = SAFE_MALLOC(page_size); + memset(page_data, 'a', page_size); +} + +static void cleanup(void) +{ + free(page_data); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .bufs = (struct tst_buffers []) { + {&cs, .size = sizeof(struct cachestat)}, + {&cs_range, .size = sizeof(struct cachestat_range)}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/cachestat/cachestat03.c b/testcases/kernel/syscalls/cachestat/cachestat03.c new file mode 100644 index 00000000..7c3abb3a --- /dev/null +++ b/testcases/kernel/syscalls/cachestat/cachestat03.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that cachestat() syscall is properly failing with relative + * error codes according to input parameters. + * + * - EFAULT: cstat or cstat_range points to an illegal address + * - EINVAL: invalid flags + * - EBADF: invalid file descriptor + * - EOPNOTSUPP: file descriptor is of a hugetlbfs file + */ + +#define MNTPOINT "mnt" + +#include "cachestat.h" + +static int fd; +static int fd_hugepage; +static int invalid_fd = -1; +static struct cachestat *cs; +static struct cachestat *cs_null; +static struct cachestat_range *cs_range; +static struct cachestat_range *cs_range_null; + +static struct tcase { + int *fd; + struct cachestat_range **range; + struct cachestat **data; + int flags; + int exp_errno; + char *msg; +} tcases[] = { + {&invalid_fd, &cs_range, &cs, 0, EBADF, "Invalid fd (-1)"}, + {&fd, &cs_range_null, &cs, 0, EFAULT, "Invalid range (NULL)"}, + {&fd, &cs_range, &cs_null, 0, EFAULT, "Invalid data (NULL)"}, + {&fd, &cs_range, &cs, -1, EINVAL, "Invalid args (-1)"}, + {&fd_hugepage, &cs_range, &cs, 0, EOPNOTSUPP, "Unsupported hugetlbfs"}, +}; + +static void run(unsigned int i) +{ + struct tcase *tc = &tcases[i]; + + TST_EXP_FAIL(cachestat(*tc->fd, *tc->range, *tc->data, tc->flags), + tc->exp_errno, "%s", tc->msg); +} + +static void setup(void) +{ + fd = SAFE_OPEN("test", O_CREAT | O_RDWR, 0700); + fd_hugepage = SAFE_OPEN(MNTPOINT"/test", O_CREAT | O_RDWR, 0700); +} + +static void cleanup(void) +{ + SAFE_CLOSE(fd); + SAFE_CLOSE(fd_hugepage); +} + +static struct tst_test test = { + .test = run, + .setup = setup, + .cleanup = cleanup, + .mntpoint = MNTPOINT, + .needs_hugetlbfs = 1, + .hugepages = {1, TST_NEEDS}, + .tcnt = ARRAY_SIZE(tcases), + .needs_tmpdir = 1, + .bufs = (struct tst_buffers []) { + {&cs, .size = sizeof(struct cachestat)}, + {&cs_range, .size = sizeof(struct cachestat_range)}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/cachestat/cachestat04.c b/testcases/kernel/syscalls/cachestat/cachestat04.c new file mode 100644 index 00000000..d33769bf --- /dev/null +++ b/testcases/kernel/syscalls/cachestat/cachestat04.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies cachestat() for all the possible file descriptors, + * checking that cache statistics are always zero, except for unsupported file + * descriptors which cause EBADF to be raised. + */ + +#include "tst_test.h" +#include "lapi/mman.h" + +#define MNTPOINT "mnt" + +static struct cachestat *cs; +static struct cachestat_range *cs_range; + +static void check_cachestat(struct tst_fd *fd_in) +{ + int ret; + + memset(cs, 0xff, sizeof(*cs)); + + ret = cachestat(fd_in->fd, cs_range, cs, 0); + if (ret == -1) { + TST_EXP_EQ_LI(errno, EBADF); + return; + } + + TST_EXP_EQ_LI(cs->nr_cache, 0); + TST_EXP_EQ_LI(cs->nr_dirty, 0); + TST_EXP_EQ_LI(cs->nr_writeback, 0); + TST_EXP_EQ_LI(cs->nr_evicted, 0); + TST_EXP_EQ_LI(cs->nr_recently_evicted, 0); +} + +static void run(void) +{ + TST_FD_FOREACH(fd) { + tst_res(TINFO, "%s -> ...", tst_fd_desc(&fd)); + check_cachestat(&fd); + } +} + +static struct tst_test test = { + .timeout = 2, + .test_all = run, + .mount_device = 1, + .mntpoint = MNTPOINT, + .bufs = (struct tst_buffers []) { + {&cs, .size = sizeof(struct cachestat)}, + {&cs_range, .size = sizeof(struct cachestat_range)}, + {} + }, +}; + diff --git a/testcases/kernel/syscalls/capset/capset01.c b/testcases/kernel/syscalls/capset/capset01.c index 25db8112..a529ca99 100755 --- a/testcases/kernel/syscalls/capset/capset01.c +++ b/testcases/kernel/syscalls/capset/capset01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Author: Saji Kumar.V.R @@ -10,8 +10,6 @@ */ /*\ - * [Description] - * * Test capset() with with LINUX_CAPABILITY_VERSION_{1,2,3}. */ diff --git a/testcases/kernel/syscalls/capset/capset02.c b/testcases/kernel/syscalls/capset/capset02.c index 989f3e4c..f9d4d860 100755 --- a/testcases/kernel/syscalls/capset/capset02.c +++ b/testcases/kernel/syscalls/capset/capset02.c @@ -6,21 +6,15 @@ */ /*\ - * [Description] - * Tests basic error handling of the capset syscall. + * Verify that, capset(2) fails and sets errno to * - * 1. capset() fails with errno set to EFAULT if an invalid address - * is given for header. - * 2. capset() fails with errno set to EFAULT if an invalid address - * is given for data. - * 3. capset() fails with errno set to EINVAL if an invalid value - * is given for header->version. - * 4. capset() fails with errno set to EPERM if the new_Effective is - * not a subset of the new_Permitted. - * 5. capset() fails with errno set to EPERM if the new_Permitted is - * not a subset of the old_Permitted. - * 6. capset() fails with errno set ot EPERM if the new_Inheritable is - * not a subset of the old_Inheritable and bounding set. + * - EFAULT if an invalid address is given for header. + * - EFAULT if an invalid address is given for data. + * - EINVAL if an invalid value is given for header->version. + * - EPERM if the new_Effective is not a subset of the new_Permitted. + * - EPERM if the new_Permitted is not a subset of the old_Permitted. + * - EPERM if the new_Inheritable is not a subset of the + * old_Inheritable and bounding set. */ #include diff --git a/testcases/kernel/syscalls/capset/capset03.c b/testcases/kernel/syscalls/capset/capset03.c index 2b83e6ce..a75807ff 100755 --- a/testcases/kernel/syscalls/capset/capset03.c +++ b/testcases/kernel/syscalls/capset/capset03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * capset() fails with errno set or EPERM if the new_Inheritable is * not a subset of old_Inheritable and old_Permitted without CAP_SETPCAP. */ diff --git a/testcases/kernel/syscalls/capset/capset04.c b/testcases/kernel/syscalls/capset/capset04.c index 13477a4a..abe6f8fe 100755 --- a/testcases/kernel/syscalls/capset/capset04.c +++ b/testcases/kernel/syscalls/capset/capset04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test whether capset() can be used to modify the capabilities of a thread * other than itself. Now, most linux distributions with kernel supporting * VFS capabilities, this should be never permitted. diff --git a/testcases/kernel/syscalls/chdir/chdir01.c b/testcases/kernel/syscalls/chdir/chdir01.c index d50a8f50..1c129933 100755 --- a/testcases/kernel/syscalls/chdir/chdir01.c +++ b/testcases/kernel/syscalls/chdir/chdir01.c @@ -149,6 +149,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 10, .needs_root = 1, .mount_device = 1, .mntpoint = MNTPOINT, diff --git a/testcases/kernel/syscalls/chdir/chdir04.c b/testcases/kernel/syscalls/chdir/chdir04.c index cdbb2966..6e53b7fe 100755 --- a/testcases/kernel/syscalls/chdir/chdir04.c +++ b/testcases/kernel/syscalls/chdir/chdir04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Testcase to test whether chdir(2) sets errno correctly. */ diff --git a/testcases/kernel/syscalls/chmod/.gitignore b/testcases/kernel/syscalls/chmod/.gitignore index 27ddfce1..22e6c7bc 100755 --- a/testcases/kernel/syscalls/chmod/.gitignore +++ b/testcases/kernel/syscalls/chmod/.gitignore @@ -3,3 +3,5 @@ /chmod05 /chmod06 /chmod07 +/chmod08 +/chmod09 diff --git a/testcases/kernel/syscalls/chmod/chmod01.c b/testcases/kernel/syscalls/chmod/chmod01.c index b3b828ac..8598acf1 100755 --- a/testcases/kernel/syscalls/chmod/chmod01.c +++ b/testcases/kernel/syscalls/chmod/chmod01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that chmod(2) succeeds when used to change the mode permissions * of a file or directory. */ diff --git a/testcases/kernel/syscalls/chmod/chmod03.c b/testcases/kernel/syscalls/chmod/chmod03.c index 3ceeae26..d4056ac8 100755 --- a/testcases/kernel/syscalls/chmod/chmod03.c +++ b/testcases/kernel/syscalls/chmod/chmod03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that, chmod(2) will succeed to change the mode of a file or directory * and set the sticky bit on it if invoked by non-root (uid != 0) * process with the following constraints: diff --git a/testcases/kernel/syscalls/chmod/chmod05.c b/testcases/kernel/syscalls/chmod/chmod05.c index f5b9d51e..931e58a0 100755 --- a/testcases/kernel/syscalls/chmod/chmod05.c +++ b/testcases/kernel/syscalls/chmod/chmod05.c @@ -81,8 +81,7 @@ static void setup(void) * gid. */ SAFE_MKDIR(TESTDIR, MODE_RWX); - if (setgroups(1, &nobody_u->pw_gid) == -1) - tst_brk(TBROK | TERRNO, "setgroups to nobody's gid failed"); + SAFE_SETGROUPS(1, &nobody_u->pw_gid); SAFE_CHOWN(TESTDIR, nobody_u->pw_uid, free_gid); diff --git a/testcases/kernel/syscalls/chmod/chmod06.c b/testcases/kernel/syscalls/chmod/chmod06.c index d6b86af7..40255184 100755 --- a/testcases/kernel/syscalls/chmod/chmod06.c +++ b/testcases/kernel/syscalls/chmod/chmod06.c @@ -5,25 +5,18 @@ * Copyright (c) 2014-2018 Cyril Hrubis */ -/* - * Test Name: chmod06 +/*\ + * Verify that, chmod(2) returns -1 and sets errno to * - * Test Description: - * Verify that, - * 1) chmod(2) returns -1 and sets errno to EPERM if the effective user id - * of process does not match the owner of the file and the process is - * not super user. - * 2) chmod(2) returns -1 and sets errno to EACCES if search permission is - * denied on a component of the path prefix. - * 3) chmod(2) returns -1 and sets errno to EFAULT if pathname points - * outside user's accessible address space. - * 4) chmod(2) returns -1 and sets errno to ENAMETOOLONG if the pathname - * component is too long. - * 5) chmod(2) returns -1 and sets errno to ENOTDIR if the directory - * component in pathname is not a directory. - * 6) chmod(2) returns -1 and sets errno to ENOENT if the specified file - * does not exists. + * - EPERM if the effective user id of process does not match the owner of the + * file and the process is not super user + * - EACCES if search permission is denied on a component of the path prefix + * - EFAULT if pathname points outside user's accessible address space + * - ENAMETOOLONG if the pathname component is too long + * - ENOTDIR if the directory component in pathname is not a directory + * - ENOENT if the specified file does not exists */ + #include #include #include "tst_test.h" diff --git a/testcases/kernel/syscalls/chmod/chmod08.c b/testcases/kernel/syscalls/chmod/chmod08.c new file mode 100644 index 00000000..ab80e02f --- /dev/null +++ b/testcases/kernel/syscalls/chmod/chmod08.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Authors: David Fenner, Jon Hendrickson + * Copyright (C) 2024 Andrea Cervesato + */ + +/*\ + * Test verifies that chmod() is working correctly on symlink() + * generated files. + */ + +#include "tst_test.h" + +#define PERMS 01777 +#define TESTFILE "myobject" +#define SYMBNAME "my_symlink0" + +static void run(void) +{ + struct stat oldsym_stat; + struct stat newsym_stat; + + SAFE_TOUCH(TESTFILE, 0644, NULL); + SAFE_SYMLINK(TESTFILE, SYMBNAME); + SAFE_STAT(SYMBNAME, &oldsym_stat); + + TST_EXP_PASS(chmod(SYMBNAME, PERMS)); + SAFE_STAT(SYMBNAME, &newsym_stat); + + TST_EXP_EQ_LI(newsym_stat.st_mode & PERMS, PERMS); + TST_EXP_EXPR(oldsym_stat.st_mode != newsym_stat.st_mode, + "file mode has changed"); + + SAFE_UNLINK(SYMBNAME); + SAFE_UNLINK(TESTFILE); +} + +static struct tst_test test = { + .test_all = run, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/chmod/chmod09.c b/testcases/kernel/syscalls/chmod/chmod09.c new file mode 100644 index 00000000..99060088 --- /dev/null +++ b/testcases/kernel/syscalls/chmod/chmod09.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Wei Gao + */ + +/*\ + * Test for kernel commit + * 5d1f903f75a8 ("attr: block mode changes of symlinks") + */ + +#include "lapi/fcntl.h" +#include "tst_test.h" + +#define MODE 0644 +#define TESTFILE "testfile" +#define TESTFILE_SYMLINK "testfile_symlink" + +static void run(void) +{ + struct stat stat_file, stat_sym; + int mode = 0; + char fd_path[100]; + + int fd = SAFE_OPEN(TESTFILE_SYMLINK, O_PATH | O_NOFOLLOW); + + sprintf(fd_path, "/proc/self/fd/%d", fd); + + TST_EXP_FAIL(chmod(fd_path, mode), ENOTSUP, "chmod(%s, %04o)", + TESTFILE_SYMLINK, mode); + + SAFE_STAT(TESTFILE, &stat_file); + SAFE_LSTAT(TESTFILE_SYMLINK, &stat_sym); + + stat_file.st_mode &= ~S_IFREG; + stat_sym.st_mode &= ~S_IFLNK; + + TST_EXP_EXPR(stat_file.st_mode != (unsigned int)mode, + "stat(%s) mode=%04o", TESTFILE, stat_file.st_mode); + + TST_EXP_EXPR(stat_sym.st_mode != (unsigned int)mode, + "stat(%s) mode=%04o", TESTFILE, stat_sym.st_mode); + + SAFE_CLOSE(fd); +} + +static void setup(void) +{ + SAFE_TOUCH(TESTFILE, MODE, NULL); + SAFE_SYMLINK(TESTFILE, TESTFILE_SYMLINK); +} + +static void cleanup(void) +{ + remove(TESTFILE); + remove(TESTFILE_SYMLINK); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .min_kver = "6.6", + .mntpoint = "mntpoint", + .all_filesystems = 1, + .tags = (const struct tst_tag[]) { + {"linux-git", "5d1f903f75a8"}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/chown/chown01.c b/testcases/kernel/syscalls/chown/chown01.c index 7fbb116b..18406cf1 100755 --- a/testcases/kernel/syscalls/chown/chown01.c +++ b/testcases/kernel/syscalls/chown/chown01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * AUTHOR: William Roske @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Basic test for chown(). Calls chown() on a file and expects it to pass. */ diff --git a/testcases/kernel/syscalls/chown/chown02.c b/testcases/kernel/syscalls/chown/chown02.c index 46718583..000433da 100755 --- a/testcases/kernel/syscalls/chown/chown02.c +++ b/testcases/kernel/syscalls/chown/chown02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that chown(2) invoked by super-user: * * - clears setuid and setgid bits set on an executable file diff --git a/testcases/kernel/syscalls/chown/chown03.c b/testcases/kernel/syscalls/chown/chown03.c index b4ca3af7..dd0c9dd0 100755 --- a/testcases/kernel/syscalls/chown/chown03.c +++ b/testcases/kernel/syscalls/chown/chown03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that, chown(2) succeeds to change the group of a file specified * by path when called by non-root user with the following constraints: * diff --git a/testcases/kernel/syscalls/chown/chown04.c b/testcases/kernel/syscalls/chown/chown04.c index 4e918856..f7a15133 100755 --- a/testcases/kernel/syscalls/chown/chown04.c +++ b/testcases/kernel/syscalls/chown/chown04.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that: * * 1. Chown() returns -1 and sets errno to EPERM if the effective user id diff --git a/testcases/kernel/syscalls/chown/chown05.c b/testcases/kernel/syscalls/chown/chown05.c index 44abdc75..612d6c1e 100755 --- a/testcases/kernel/syscalls/chown/chown05.c +++ b/testcases/kernel/syscalls/chown/chown05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, chown(2) succeeds to change the owner and group of a file * specified by path to any numeric owner(uid)/group(gid) values when invoked * by super-user. diff --git a/testcases/kernel/syscalls/chroot/chroot01.c b/testcases/kernel/syscalls/chroot/chroot01.c index febf064d..fa5172de 100755 --- a/testcases/kernel/syscalls/chroot/chroot01.c +++ b/testcases/kernel/syscalls/chroot/chroot01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Testcase to check the whether chroot sets errno to EPERM. * * As a non-root user attempt to perform chroot() to a directory. The @@ -27,18 +25,12 @@ static void setup(void) { struct passwd *ltpuser; - path = tst_get_tmpdir(); + path = tst_tmpdir_path(); ltpuser = SAFE_GETPWNAM("nobody"); SAFE_SETEUID(ltpuser->pw_uid); } -static void cleanup(void) -{ - free(path); -} - static struct tst_test test = { - .cleanup = cleanup, .setup = setup, .test_all = verify_chroot, .needs_root = 1, diff --git a/testcases/kernel/syscalls/chroot/chroot02.c b/testcases/kernel/syscalls/chroot/chroot02.c index ad33abdc..1b4fa2f4 100755 --- a/testcases/kernel/syscalls/chroot/chroot02.c +++ b/testcases/kernel/syscalls/chroot/chroot02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Basic chroot() functionality test. * * - Create a file in the temporary directory @@ -37,17 +35,11 @@ static void verify_chroot(void) static void setup(void) { - path = tst_get_tmpdir(); + path = tst_tmpdir_path(); SAFE_TOUCH(TMP_FILENAME, 0666, NULL); } -static void cleanup(void) -{ - free(path); -} - static struct tst_test test = { - .cleanup = cleanup, .setup = setup, .test_all = verify_chroot, .needs_root = 1, diff --git a/testcases/kernel/syscalls/chroot/chroot03.c b/testcases/kernel/syscalls/chroot/chroot03.c index 87faec31..4fe06ec1 100755 --- a/testcases/kernel/syscalls/chroot/chroot03.c +++ b/testcases/kernel/syscalls/chroot/chroot03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Testcase to test whether chroot(2) sets errno correctly. * * - to test whether chroot() is setting ENAMETOOLONG if the diff --git a/testcases/kernel/syscalls/chroot/chroot04.c b/testcases/kernel/syscalls/chroot/chroot04.c index ed0f6632..c0e7afce 100755 --- a/testcases/kernel/syscalls/chroot/chroot04.c +++ b/testcases/kernel/syscalls/chroot/chroot04.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Testcase to check that chroot sets errno to EACCES. * * As a non-root user attempt to perform chroot() to a directory that the user diff --git a/testcases/kernel/syscalls/clock_adjtime/clock_adjtime.h b/testcases/kernel/syscalls/clock_adjtime/clock_adjtime.h index dbe0a561..8250cb92 100755 --- a/testcases/kernel/syscalls/clock_adjtime/clock_adjtime.h +++ b/testcases/kernel/syscalls/clock_adjtime/clock_adjtime.h @@ -4,6 +4,9 @@ * Author: Rafael David Tinoco */ +#ifndef CLOCK_ADJTIME_H__ +#define CLOCK_ADJTIME_H__ + #include "config.h" #include "tst_test.h" #include "tst_timer.h" @@ -246,3 +249,5 @@ TIMEX_GET_SET_FIELD_TYPE(uint, uint); TIMEX_GET_SET_FIELD_TYPE(long, long long); #undef TIMEX_GET_SET_FIELD_TYPE + +#endif /* CLOCK_ADJTIME_H__ */ diff --git a/testcases/kernel/syscalls/clock_adjtime/clock_adjtime02.c b/testcases/kernel/syscalls/clock_adjtime/clock_adjtime02.c index 2df9099d..eaa7f807 100755 --- a/testcases/kernel/syscalls/clock_adjtime/clock_adjtime02.c +++ b/testcases/kernel/syscalls/clock_adjtime/clock_adjtime02.c @@ -61,11 +61,14 @@ static long hz; static struct tst_timex saved, ttxc; static int supported; static void *bad_addr; +static clockid_t max_clocks1; +static clockid_t max_clocks2; +static clockid_t clk_realtime = CLOCK_REALTIME; static void cleanup(void); struct test_case { - clockid_t clktype; + clockid_t *clktype; unsigned int modes; long lowlimit; long highlimit; @@ -74,36 +77,36 @@ struct test_case { int droproot; }; -struct test_case tc[] = { +static struct test_case tc[] = { { - .clktype = MAX_CLOCKS, + .clktype = &max_clocks1, .exp_err = EINVAL, }, { - .clktype = MAX_CLOCKS + 1, + .clktype = &max_clocks2, .exp_err = EINVAL, }, { - .clktype = CLOCK_REALTIME, + .clktype = &clk_realtime, .modes = ADJ_ALL, .exp_err = EFAULT, }, { - .clktype = CLOCK_REALTIME, + .clktype = &clk_realtime, .modes = ADJ_TICK, .lowlimit = 900000, .delta = 1, .exp_err = EINVAL, }, { - .clktype = CLOCK_REALTIME, + .clktype = &clk_realtime, .modes = ADJ_TICK, .highlimit = 1100000, .delta = 1, .exp_err = EINVAL, }, { - .clktype = CLOCK_REALTIME, + .clktype = &clk_realtime, .modes = ADJ_ALL, .exp_err = EPERM, .droproot = 1, @@ -162,9 +165,9 @@ static void verify_clock_adjtime(unsigned int i) /* special case: EFAULT for bad addresses */ if (tc[i].exp_err == EFAULT) { - TEST(tv->clock_adjtime(tc[i].clktype, bad_addr)); + TEST(tv->clock_adjtime(*tc[i].clktype, bad_addr)); } else { - TEST(tv->clock_adjtime(tc[i].clktype, tst_timex_get(txcptr))); + TEST(tv->clock_adjtime(*tc[i].clktype, tst_timex_get(txcptr))); timex_show("TEST", txcptr); } @@ -223,6 +226,9 @@ static void setup(void) tc[i].lowlimit /= hz; } } + + max_clocks1 = tst_get_max_clocks(); + max_clocks2 = max_clocks1 + 1; } static void cleanup(void) diff --git a/testcases/kernel/syscalls/clock_gettime/Makefile b/testcases/kernel/syscalls/clock_gettime/Makefile index e7f5e9e7..2d914460 100755 --- a/testcases/kernel/syscalls/clock_gettime/Makefile +++ b/testcases/kernel/syscalls/clock_gettime/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpvdso +LTPLIBS = vdso include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/clock_gettime/clock_gettime01.c b/testcases/kernel/syscalls/clock_gettime/clock_gettime01.c index a67639b0..3bfe60fd 100755 --- a/testcases/kernel/syscalls/clock_gettime/clock_gettime01.c +++ b/testcases/kernel/syscalls/clock_gettime/clock_gettime01.c @@ -3,17 +3,18 @@ * Copyright (c) 2019 Linaro Limited. All rights reserved. * Author: Rafael David Tinoco */ -/* + +/*\ * Basic test for clock_gettime(2) on multiple clocks: * - * 1) CLOCK_REALTIME - * 2) CLOCK_MONOTONIC - * 3) CLOCK_PROCESS_CPUTIME_ID - * 4) CLOCK_THREAD_CPUTIME_ID - * 5) CLOCK_REALTIME_COARSE - * 6) CLOCK_MONOTONIC_COARSE - * 7) CLOCK_MONOTONIC_RAW - * 8) CLOCK_BOOTTIME + * #. CLOCK_REALTIME + * #. CLOCK_MONOTONIC + * #. CLOCK_PROCESS_CPUTIME_ID + * #. CLOCK_THREAD_CPUTIME_ID + * #. CLOCK_REALTIME_COARSE + * #. CLOCK_MONOTONIC_COARSE + * #. CLOCK_MONOTONIC_RAW + * #. CLOCK_BOOTTIME */ #include "config.h" diff --git a/testcases/kernel/syscalls/clock_gettime/clock_gettime02.c b/testcases/kernel/syscalls/clock_gettime/clock_gettime02.c index 1159dbd1..9b0b970d 100755 --- a/testcases/kernel/syscalls/clock_gettime/clock_gettime02.c +++ b/testcases/kernel/syscalls/clock_gettime/clock_gettime02.c @@ -3,19 +3,20 @@ * Copyright (c) 2019 Linaro Limited. All rights reserved. * Author: Rafael David Tinoco */ -/* + +/*\ * Bad argument tests for clock_gettime(2) on multiple clocks: * - * 1) MAX_CLOCKS - * 2) MAX_CLOCKS + 1 - * 3) CLOCK_REALTIME - * 4) CLOCK_MONOTONIC - * 5) CLOCK_PROCESS_CPUTIME_ID - * 6) CLOCK_THREAD_CPUTIME_ID - * 7) CLOCK_REALTIME_COARSE - * 8) CLOCK_MONOTONIC_COARSE - * 9) CLOCK_MONOTONIC_RAW - * 10) CLOCK_BOOTTIME + * #. MAX_CLOCKS + * #. MAX_CLOCKS + 1 + * #. CLOCK_REALTIME + * #. CLOCK_MONOTONIC + * #. CLOCK_PROCESS_CPUTIME_ID + * #. CLOCK_THREAD_CPUTIME_ID + * #. CLOCK_REALTIME_COARSE + * #. CLOCK_MONOTONIC_COARSE + * #. CLOCK_MONOTONIC_RAW + * #. CLOCK_BOOTTIME */ #include "config.h" @@ -24,20 +25,30 @@ #include "tst_safe_clocks.h" static void *bad_addr; +static clockid_t max_clocks1; +static clockid_t max_clocks2; +static clockid_t clk_realtime = CLOCK_REALTIME; +static clockid_t clk_monotonic = CLOCK_MONOTONIC; +static clockid_t clk_process_cputime_id = CLOCK_PROCESS_CPUTIME_ID; +static clockid_t clk_thread_cputime_id = CLOCK_THREAD_CPUTIME_ID; +static clockid_t clk_realtime_coarse = CLOCK_REALTIME_COARSE; +static clockid_t clk_monotonic_coarse = CLOCK_MONOTONIC_COARSE; +static clockid_t clk_monotonic_raw = CLOCK_MONOTONIC_RAW; +static clockid_t clk_boottime = CLOCK_BOOTTIME; struct test_case { - clockid_t clktype; + clockid_t *clktype; int exp_err; int allow_inval; }; static struct test_case tc[] = { { - .clktype = MAX_CLOCKS, + .clktype = &max_clocks1, .exp_err = EINVAL, }, { - .clktype = MAX_CLOCKS + 1, + .clktype = &max_clocks2, .exp_err = EINVAL, }, /* @@ -45,38 +56,38 @@ static struct test_case tc[] = { * It justifies testing EFAULT for all. */ { - .clktype = CLOCK_REALTIME, + .clktype = &clk_realtime, .exp_err = EFAULT, }, { - .clktype = CLOCK_MONOTONIC, + .clktype = &clk_monotonic, .exp_err = EFAULT, }, { - .clktype = CLOCK_PROCESS_CPUTIME_ID, + .clktype = &clk_process_cputime_id, .exp_err = EFAULT, }, { - .clktype = CLOCK_THREAD_CPUTIME_ID, + .clktype = &clk_thread_cputime_id, .exp_err = EFAULT, }, { - .clktype = CLOCK_REALTIME_COARSE, + .clktype = &clk_realtime_coarse, .exp_err = EFAULT, .allow_inval = 1, }, { - .clktype = CLOCK_MONOTONIC_COARSE, + .clktype = &clk_monotonic_coarse, .exp_err = EFAULT, .allow_inval = 1, }, { - .clktype = CLOCK_MONOTONIC_RAW, + .clktype = &clk_monotonic_raw, .exp_err = EFAULT, .allow_inval = 1, }, { - .clktype = CLOCK_BOOTTIME, + .clktype = &clk_boottime, .exp_err = EFAULT, .allow_inval = 1, }, @@ -102,6 +113,9 @@ static void setup(void) tst_res(TINFO, "Testing variant: %d: %s", tst_variant, variants[tst_variant].desc); bad_addr = tst_get_bad_addr(NULL); + + max_clocks1 = tst_get_max_clocks(); + max_clocks2 = max_clocks1 + 1; } static void verify_clock_gettime(unsigned int i) @@ -117,21 +131,21 @@ static void verify_clock_gettime(unsigned int i) ts = tst_ts_get(&spec); } - TEST(tv->clock_gettime(tc[i].clktype, ts)); + TEST(tv->clock_gettime(*tc[i].clktype, ts)); if (TST_RET != -1) { tst_res(TFAIL, "clock_gettime(2): clock %s passed unexpectedly", - tst_clock_name(tc[i].clktype)); + tst_clock_name(*tc[i].clktype)); return; } if ((tc[i].exp_err == TST_ERR) || (tc[i].allow_inval && TST_ERR == EINVAL)) { tst_res(TPASS | TTERRNO, "clock_gettime(2): clock %s failed as expected", - tst_clock_name(tc[i].clktype)); + tst_clock_name(*tc[i].clktype)); } else { tst_res(TFAIL | TTERRNO, "clock_gettime(2): clock %s failed unexpectedly", - tst_clock_name(tc[i].clktype)); + tst_clock_name(*tc[i].clktype)); } } diff --git a/testcases/kernel/syscalls/clock_gettime/clock_gettime03.c b/testcases/kernel/syscalls/clock_gettime/clock_gettime03.c index b02d22a1..495030c0 100755 --- a/testcases/kernel/syscalls/clock_gettime/clock_gettime03.c +++ b/testcases/kernel/syscalls/clock_gettime/clock_gettime03.c @@ -3,9 +3,7 @@ * Copyright (c) 2020 Cyril Hrubis */ -/* - * [Description] - * +/*\ * After a call to unshare(CLONE_NEWTIME) a new timer namespace is created, the * process that has called the unshare() can adjust offsets for CLOCK_MONOTONIC * and CLOCK_BOOTTIME for its children by writing to the '/proc/self/timens_offsets'. diff --git a/testcases/kernel/syscalls/clock_gettime/clock_gettime04.c b/testcases/kernel/syscalls/clock_gettime/clock_gettime04.c index c279da79..4f262da0 100755 --- a/testcases/kernel/syscalls/clock_gettime/clock_gettime04.c +++ b/testcases/kernel/syscalls/clock_gettime/clock_gettime04.c @@ -5,18 +5,11 @@ */ /*\ - * [Description] - * * Check time difference between successive readings and report a bug if * difference found to be over 5 ms. * - * This test reports a s390x BUG which has been fixed in: - * - * commit 5b43bd184530af6b868d8273b0a743a138d37ee8 - * Author: Heiko Carstens - * Date: Wed Mar 24 20:23:55 2021 +0100 - * - * s390/vdso: fix initializing and updating of vdso_data + * This test reports a s390x BUG which has been fixed in kernel v5.12 in + * 5b43bd184530 ("s390/vdso: fix initializing and updating of vdso_data") */ #include "config.h" diff --git a/testcases/kernel/syscalls/clock_gettime/leapsec01.c b/testcases/kernel/syscalls/clock_gettime/leapsec01.c index 73078fb6..f6db4e81 100755 --- a/testcases/kernel/syscalls/clock_gettime/leapsec01.c +++ b/testcases/kernel/syscalls/clock_gettime/leapsec01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Red Hat, Inc., 2012. * Copyright (c) Linux Test Project, 2019 @@ -8,7 +8,8 @@ * Ported to new library: * 07/2019 Christian Amann */ -/* + +/*\ * Regression test for hrtimer early expiration during and after leap seconds * * A bug in the hrtimer subsystem caused all TIMER_ABSTIME CLOCK_REALTIME @@ -196,6 +197,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 40, .test_all = run_leapsec, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep01.c b/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep01.c index eef8a599..55e7b294 100755 --- a/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep01.c +++ b/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep01.c @@ -219,6 +219,7 @@ static void do_test(unsigned int i) } static struct tst_test test = { + .timeout = 3, .tcnt = ARRAY_SIZE(tcase), .test = do_test, .test_variants = ARRAY_SIZE(variants), diff --git a/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep03.c b/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep03.c index dfc52227..1b097a60 100755 --- a/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep03.c +++ b/testcases/kernel/syscalls/clock_nanosleep/clock_nanosleep03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test that clock_nanosleep() adds correctly an offset with absolute timeout * and CLOCK_MONOTONIC inside of a timer namespace. * diff --git a/testcases/kernel/syscalls/clock_settime/.gitignore b/testcases/kernel/syscalls/clock_settime/.gitignore index b66169b3..8bcc83d6 100755 --- a/testcases/kernel/syscalls/clock_settime/.gitignore +++ b/testcases/kernel/syscalls/clock_settime/.gitignore @@ -1,3 +1,4 @@ clock_settime01 clock_settime02 clock_settime03 +clock_settime04 diff --git a/testcases/kernel/syscalls/clock_settime/clock_settime02.c b/testcases/kernel/syscalls/clock_settime/clock_settime02.c index 5974e79a..c1bd3554 100755 --- a/testcases/kernel/syscalls/clock_settime/clock_settime02.c +++ b/testcases/kernel/syscalls/clock_settime/clock_settime02.c @@ -16,75 +16,84 @@ #define DELTA_SEC 10 static void *bad_addr; +static clockid_t max_clocks1; +static clockid_t max_clocks2; +static clockid_t clk_realtime = CLOCK_REALTIME; +static clockid_t clk_monotonic = CLOCK_MONOTONIC; +static clockid_t clk_process_cputime_id = CLOCK_PROCESS_CPUTIME_ID; +static clockid_t clk_thread_cputime_id = CLOCK_THREAD_CPUTIME_ID; +static clockid_t clk_monotonic_coarse = CLOCK_MONOTONIC_COARSE; +static clockid_t clk_monotonic_raw = CLOCK_MONOTONIC_RAW; +static clockid_t clk_boottime = CLOCK_BOOTTIME; struct test_case { - clockid_t type; + clockid_t *type; int exp_err; int replace; long tv_sec; long tv_nsec; }; -struct test_case tc[] = { +static struct test_case tc[] = { { /* case 01: REALTIME: timespec NULL */ - .type = CLOCK_REALTIME, + .type = &clk_realtime, .exp_err = EFAULT, .replace = 1, .tv_sec = 0, .tv_nsec = 0, }, { /* case 02: REALTIME: tv_sec = -1 */ - .type = CLOCK_REALTIME, + .type = &clk_realtime, .exp_err = EINVAL, .replace = 1, .tv_sec = -1, .tv_nsec = 0, }, { /* case 03: REALTIME: tv_nsec = -1 */ - .type = CLOCK_REALTIME, + .type = &clk_realtime, .exp_err = EINVAL, .replace = 1, .tv_sec = 0, .tv_nsec = -1, }, { /* case 04: REALTIME: tv_nsec = 1s+1 */ - .type = CLOCK_REALTIME, + .type = &clk_realtime, .exp_err = EINVAL, .replace = 1, .tv_sec = 0, .tv_nsec = NSEC_PER_SEC + 1, }, { /* case 05: MONOTONIC */ - .type = CLOCK_MONOTONIC, + .type = &clk_monotonic, .exp_err = EINVAL, }, { /* case 06: MAXCLOCK */ - .type = MAX_CLOCKS, + .type = &max_clocks1, .exp_err = EINVAL, }, { /* case 07: MAXCLOCK+1 */ - .type = MAX_CLOCKS + 1, + .type = &max_clocks2, .exp_err = EINVAL, }, /* Linux specific */ { /* case 08: CLOCK_MONOTONIC_COARSE */ - .type = CLOCK_MONOTONIC_COARSE, + .type = &clk_monotonic_coarse, .exp_err = EINVAL, }, { /* case 09: CLOCK_MONOTONIC_RAW */ - .type = CLOCK_MONOTONIC_RAW, + .type = &clk_monotonic_raw, .exp_err = EINVAL, }, { /* case 10: CLOCK_BOOTTIME */ - .type = CLOCK_BOOTTIME, + .type = &clk_boottime, .exp_err = EINVAL, }, { /* case 11: CLOCK_PROCESS_CPUTIME_ID */ - .type = CLOCK_PROCESS_CPUTIME_ID, + .type = &clk_process_cputime_id, .exp_err = EINVAL, }, { /* case 12: CLOCK_THREAD_CPUTIME_ID */ - .type = CLOCK_THREAD_CPUTIME_ID, + .type = &clk_thread_cputime_id, .exp_err = EINVAL, }, }; @@ -106,6 +115,9 @@ static void setup(void) tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc); bad_addr = tst_get_bad_addr(NULL); + + max_clocks1 = tst_get_max_clocks(); + max_clocks2 = max_clocks1 + 1; } static void verify_clock_settime(unsigned int i) @@ -137,23 +149,23 @@ static void verify_clock_settime(unsigned int i) else ts = tst_ts_get(&spec); - TEST(tv->clock_settime(tc[i].type, ts)); + TEST(tv->clock_settime(*tc[i].type, ts)); if (TST_RET != -1) { tst_res(TFAIL | TTERRNO, "clock_settime(2): clock %s passed unexpectedly, expected %s", - tst_clock_name(tc[i].type), + tst_clock_name(*tc[i].type), tst_strerrno(tc[i].exp_err)); return; } if (tc[i].exp_err == TST_ERR) { tst_res(TPASS | TTERRNO, "clock_settime(%s): failed as expected", - tst_clock_name(tc[i].type)); + tst_clock_name(*tc[i].type)); return; } - tst_res(TFAIL | TTERRNO, "clock_settime(2): clock %s " "expected to fail with %s", - tst_clock_name(tc[i].type), tst_strerrno(tc[i].exp_err)); + tst_res(TFAIL | TTERRNO, "clock_settime(2): clock %s expected to fail with %s", + tst_clock_name(*tc[i].type), tst_strerrno(tc[i].exp_err)); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/clock_settime/clock_settime03.c b/testcases/kernel/syscalls/clock_settime/clock_settime03.c index f196a257..692e6378 100755 --- a/testcases/kernel/syscalls/clock_settime/clock_settime03.c +++ b/testcases/kernel/syscalls/clock_settime/clock_settime03.c @@ -104,6 +104,7 @@ static void run(void) } static struct tst_test test = { + .timeout = 4, .test_all = run, .test_variants = ARRAY_SIZE(variants), .setup = setup, diff --git a/testcases/kernel/syscalls/clock_settime/clock_settime04.c b/testcases/kernel/syscalls/clock_settime/clock_settime04.c new file mode 100644 index 00000000..265e5ecc --- /dev/null +++ b/testcases/kernel/syscalls/clock_settime/clock_settime04.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Verify that changing the value of the CLOCK_REALTIME clock via + * clock_settime() shall have no effect on a thread that is blocked on + * absolute/relative clock_nanosleep(). + */ + +#include "tst_test.h" +#include "tst_timer.h" +#include "tst_safe_clocks.h" +#include "time64_variants.h" + +#define SEC_TO_US(x) (x * 1000 * 1000) + +#define CHILD_SLEEP_US SEC_TO_US(5) +#define PARENT_SLEEP_S 2 +#define DELTA_US SEC_TO_US(1) + +static struct tst_ts *sleep_child; + +static struct time64_variants variants[] = { + { + .clock_nanosleep = libc_clock_nanosleep, + .ts_type = TST_LIBC_TIMESPEC, + .desc = "vDSO or syscall with libc spec" + }, + +#if (__NR_clock_nanosleep != __LTP__NR_INVALID_SYSCALL) + { + .clock_nanosleep = sys_clock_nanosleep, + .ts_type = TST_KERN_OLD_TIMESPEC, + .desc = "syscall with old kernel spec" + }, +#endif + +#if (__NR_clock_nanosleep_time64 != __LTP__NR_INVALID_SYSCALL) + { + .clock_nanosleep = sys_clock_nanosleep64, + .ts_type = TST_KERN_TIMESPEC, + .desc = "syscall time64 with kernel spec" + }, +#endif +}; + +static void child_nanosleep(struct time64_variants *tv, const int flags) +{ + long long delta; + struct timespec begin, end; + + SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, &begin); + + if (flags & TIMER_ABSTIME) { + tst_res(TINFO, "Using absolute time sleep"); + + tst_ts_set_sec(sleep_child, begin.tv_sec); + tst_ts_set_nsec(sleep_child, begin.tv_nsec); + + *sleep_child = tst_ts_add_us(*sleep_child, CHILD_SLEEP_US); + } else { + tst_res(TINFO, "Using relative time sleep"); + + *sleep_child = tst_ts_from_us(sleep_child->type, CHILD_SLEEP_US); + } + + TEST(tv->clock_nanosleep(CLOCK_REALTIME, flags, tst_ts_get(sleep_child), NULL)); + if (TST_RET) + tst_brk(TBROK | TERRNO, "clock_nanosleep() error"); + + SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, &end); + + if (tst_timespec_lt(end, begin)) { + tst_res(TFAIL, "clock_settime() didn't sleep enough. " + "begin: %lld ms >= end: %lld ms", + tst_timespec_to_ms(begin), + tst_timespec_to_ms(end)); + return; + } + + delta = tst_timespec_abs_diff_us(begin, end); + if (!(flags & TIMER_ABSTIME)) + delta -= CHILD_SLEEP_US; + + if (delta > DELTA_US) { + tst_res(TFAIL, "parent clock_settime() affected child sleep. " + "begin: %lld ms, end: %lld ms", + tst_timespec_to_ms(begin), + tst_timespec_to_ms(end)); + return; + } + + tst_res(TPASS, "parent clock_settime() didn't affect child sleep " + "(delta time: %lld us)", delta); +} + +static void run(unsigned int tc_index) +{ + struct time64_variants *tv = &variants[tst_variant]; + struct timespec begin; + struct timespec sleep_parent = { + .tv_sec = PARENT_SLEEP_S, + }; + + if (!SAFE_FORK()) { + child_nanosleep(tv, tc_index ? TIMER_ABSTIME : 0); + exit(0); + } + + SAFE_CLOCK_GETTIME(CLOCK_REALTIME, &begin); + SAFE_CLOCK_NANOSLEEP(CLOCK_REALTIME, 0, &sleep_parent, NULL); + SAFE_CLOCK_SETTIME(CLOCK_REALTIME, &begin); +} + +static void setup(void) +{ + sleep_child->type = variants[tst_variant].ts_type; + + tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc); +} + +static struct tst_test test = { + .test = run, + .setup = setup, + .tcnt = 2, + .needs_root = 1, + .forks_child = 1, + .restore_wallclock = 1, + .test_variants = ARRAY_SIZE(variants), + .bufs = (struct tst_buffers []) { + {&sleep_child, .size = sizeof(struct tst_ts)}, + {}, + } +}; diff --git a/testcases/kernel/syscalls/clone/clone01.c b/testcases/kernel/syscalls/clone/clone01.c index 7e3f1567..cbe646e6 100755 --- a/testcases/kernel/syscalls/clone/clone01.c +++ b/testcases/kernel/syscalls/clone/clone01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic clone() test. * * Use clone() to create a child process, and wait for the child process to exit, diff --git a/testcases/kernel/syscalls/clone/clone02.c b/testcases/kernel/syscalls/clone/clone02.c index 42eea135..fd3ee1ae 100755 --- a/testcases/kernel/syscalls/clone/clone02.c +++ b/testcases/kernel/syscalls/clone/clone02.c @@ -49,11 +49,6 @@ * test failed */ -#if defined UCLINUX && !__THROW -/* workaround for libc bug */ -#define __THROW -#endif - #define _GNU_SOURCE #include @@ -64,6 +59,7 @@ #include #include "test.h" #include "safe_macros.h" +#include "tst_clone.h" #define FLAG_ALL (CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|SIGCHLD) #define FLAG_NONE SIGCHLD diff --git a/testcases/kernel/syscalls/clone/clone03.c b/testcases/kernel/syscalls/clone/clone03.c index 2c912b9d..8c343b2c 100755 --- a/testcases/kernel/syscalls/clone/clone03.c +++ b/testcases/kernel/syscalls/clone/clone03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Check for equality of getpid() from a child and return value of clone(2) */ diff --git a/testcases/kernel/syscalls/clone/clone04.c b/testcases/kernel/syscalls/clone/clone04.c index 74347e2b..30edeaeb 100755 --- a/testcases/kernel/syscalls/clone/clone04.c +++ b/testcases/kernel/syscalls/clone/clone04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that clone(2) fails with * * - EINVAL if child stack is set to NULL diff --git a/testcases/kernel/syscalls/clone/clone05.c b/testcases/kernel/syscalls/clone/clone05.c index 892a848b..56d2f8bb 100755 --- a/testcases/kernel/syscalls/clone/clone05.c +++ b/testcases/kernel/syscalls/clone/clone05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Call clone() with CLONE_VFORK flag set. verify that * execution of parent is suspended until child finishes */ diff --git a/testcases/kernel/syscalls/clone/clone06.c b/testcases/kernel/syscalls/clone/clone06.c index b32f17d9..412ebb7a 100755 --- a/testcases/kernel/syscalls/clone/clone06.c +++ b/testcases/kernel/syscalls/clone/clone06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test to verify inheritance of environment variables by child. */ diff --git a/testcases/kernel/syscalls/clone/clone07.c b/testcases/kernel/syscalls/clone/clone07.c index 8848b2b4..9ff53658 100755 --- a/testcases/kernel/syscalls/clone/clone07.c +++ b/testcases/kernel/syscalls/clone/clone07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test for a libc bug where exiting child function by returning from * it caused SIGSEGV. */ diff --git a/testcases/kernel/syscalls/clone3/clone301.c b/testcases/kernel/syscalls/clone3/clone301.c index d0fadbc5..deed30b9 100755 --- a/testcases/kernel/syscalls/clone3/clone301.c +++ b/testcases/kernel/syscalls/clone3/clone301.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic clone3() test. */ diff --git a/testcases/kernel/syscalls/clone3/clone302.c b/testcases/kernel/syscalls/clone3/clone302.c index 48b83155..9e98f195 100755 --- a/testcases/kernel/syscalls/clone3/clone302.c +++ b/testcases/kernel/syscalls/clone3/clone302.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic clone3() test to check various failures. */ diff --git a/testcases/kernel/syscalls/clone3/clone303.c b/testcases/kernel/syscalls/clone3/clone303.c index 04c41942..7e5f3adb 100644 --- a/testcases/kernel/syscalls/clone3/clone303.c +++ b/testcases/kernel/syscalls/clone3/clone303.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test case check clone3 CLONE_INTO_CGROUP flag * */ diff --git a/testcases/kernel/syscalls/close/close01.c b/testcases/kernel/syscalls/close/close01.c index f2d30a69..4e4f107e 100755 --- a/testcases/kernel/syscalls/close/close01.c +++ b/testcases/kernel/syscalls/close/close01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test that closing a file/pipe/socket works correctly. */ diff --git a/testcases/kernel/syscalls/close/close02.c b/testcases/kernel/syscalls/close/close02.c index d317cc63..2016453f 100755 --- a/testcases/kernel/syscalls/close/close02.c +++ b/testcases/kernel/syscalls/close/close02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Call close(-1) and expects it to return EBADF. */ diff --git a/testcases/kernel/syscalls/close_range/close_range01.c b/testcases/kernel/syscalls/close_range/close_range01.c index 072bbab6..60e63325 100755 --- a/testcases/kernel/syscalls/close_range/close_range01.c +++ b/testcases/kernel/syscalls/close_range/close_range01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Taken from the kernel self tests, which in turn were based on * a Syzkaller reproducer. @@ -10,8 +10,6 @@ * Copyright (c) 2021 SUSE LLC, other copyrights may apply. */ /*\ - * [Description] - * * We check that close_range() * * - closes FDs @@ -48,7 +46,7 @@ static inline void do_close_range(unsigned int fd, unsigned int max_fd, tst_brk(TCONF | TERRNO, "No CLOSE_RANGE_CLOEXEC"); } - tst_brk(TBROK | TERRNO, "close_range(%d, %d, %d)", fd, max_fd, flags); + tst_brk(TBROK | TERRNO, "close_range(%u, %u, %u)", fd, max_fd, flags); } static void setup(void) @@ -191,6 +189,7 @@ static void run(unsigned int n) } static struct tst_test test = { + .timeout = 9, .tcnt = 4, .forks_child = 1, .mount_device = 1, diff --git a/testcases/kernel/syscalls/close_range/close_range02.c b/testcases/kernel/syscalls/close_range/close_range02.c index 2aa6d2c9..f9b0cc5a 100755 --- a/testcases/kernel/syscalls/close_range/close_range02.c +++ b/testcases/kernel/syscalls/close_range/close_range02.c @@ -1,10 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2021 SUSE LLC */ /*\ - * [Description] - * * - First check close_range works on a valid range. * - Then check close_range does not accept invalid paramters. * - Then check it accepts a large lower fd. diff --git a/testcases/kernel/syscalls/cma/process_vm01.c b/testcases/kernel/syscalls/cma/process_vm01.c index 014fd6ff..730ae67e 100755 --- a/testcases/kernel/syscalls/cma/process_vm01.c +++ b/testcases/kernel/syscalls/cma/process_vm01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test errno codes in process_vm_readv and process_vm_writev syscalls. */ diff --git a/testcases/kernel/syscalls/cma/process_vm_readv02.c b/testcases/kernel/syscalls/cma/process_vm_readv02.c index 2bd66a49..1e2ac38f 100755 --- a/testcases/kernel/syscalls/cma/process_vm_readv02.c +++ b/testcases/kernel/syscalls/cma/process_vm_readv02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Fork two children, one child allocates memory and initializes it; * then the other one calls process_vm_readv and reads from the same * memory location, it then verifies if process_vm_readv returns diff --git a/testcases/kernel/syscalls/cma/process_vm_readv03.c b/testcases/kernel/syscalls/cma/process_vm_readv03.c index 4caafe86..95d35c42 100755 --- a/testcases/kernel/syscalls/cma/process_vm_readv03.c +++ b/testcases/kernel/syscalls/cma/process_vm_readv03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Fork two children, one child mallocs randomly sized trunks of memory * and initializes them; the other child calls process_vm_readv with * the remote iovecs initialized to the original process memory diff --git a/testcases/kernel/syscalls/cma/process_vm_writev02.c b/testcases/kernel/syscalls/cma/process_vm_writev02.c index 991110d2..aaf4c0b1 100755 --- a/testcases/kernel/syscalls/cma/process_vm_writev02.c +++ b/testcases/kernel/syscalls/cma/process_vm_writev02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Fork two children, the first one allocates a chunk of memory and the * other one call process_vm_writev to write known data into the first * child. Then first child verifies that the data is as expected. diff --git a/testcases/kernel/syscalls/confstr/confstr01.c b/testcases/kernel/syscalls/confstr/confstr01.c index d5cb5a47..103b8526 100755 --- a/testcases/kernel/syscalls/confstr/confstr01.c +++ b/testcases/kernel/syscalls/confstr/confstr01.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Test confstr(3) 700 (X/Open 7) functionality -- POSIX 2008. */ diff --git a/testcases/kernel/syscalls/connect/connect01.c b/testcases/kernel/syscalls/connect/connect01.c index 1c1630fa..660c4f7a 100755 --- a/testcases/kernel/syscalls/connect/connect01.c +++ b/testcases/kernel/syscalls/connect/connect01.c @@ -83,13 +83,10 @@ struct test_case_t { /* test case structure */ PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1, sizeof(struct sockaddr_in), -1, EBADF, setup0, cleanup0, "bad file descriptor"}, -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ { PF_INET, SOCK_STREAM, 0, (struct sockaddr *)-1, sizeof(struct sockaddr_in), -1, EFAULT, setup1, cleanup1, "invalid socket buffer"}, -#endif { PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1, 3, -1, EINVAL, setup1, cleanup1, "invalid salen"}, { @@ -112,10 +109,6 @@ struct test_case_t { /* test case structure */ int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); -#ifdef UCLINUX -static char *argv0; -#endif - /** * bionic's connect() implementation calls netdClientInitConnect() before * sending the request to the kernel. We need to bypass this, or the test will @@ -139,10 +132,6 @@ int main(int argc, char *argv[]) int lc; tst_parse_opts(argc, argv, NULL, NULL); -#ifdef UCLINUX - argv0 = argv[0]; - maybe_run_child(&do_child, "d", &sfd); -#endif setup(); @@ -261,13 +250,9 @@ pid_t start_server(struct sockaddr_in *sin0) } SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen); - switch ((pid = FORK_OR_VFORK())) { + switch ((pid = tst_fork())) { case 0: /* child */ -#ifdef UCLINUX - self_exec(argv0, "d", sfd); -#else do_child(); -#endif break; case -1: tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); diff --git a/testcases/kernel/syscalls/connect/connect02.c b/testcases/kernel/syscalls/connect/connect02.c index e20214e2..087b514b 100755 --- a/testcases/kernel/syscalls/connect/connect02.c +++ b/testcases/kernel/syscalls/connect/connect02.c @@ -126,6 +126,7 @@ static void run(void) } static struct tst_test test = { + .timeout = 3, .test_all = run, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c b/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c index bbcb0ca3..2390a32d 100755 --- a/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c +++ b/testcases/kernel/syscalls/copy_file_range/copy_file_range01.c @@ -232,5 +232,5 @@ static struct tst_test test = { .all_filesystems = 1, .test = copy_file_range_verify, .test_variants = TEST_VARIANTS, - .max_runtime = 5 + .timeout = 5 }; diff --git a/testcases/kernel/syscalls/creat/creat05.c b/testcases/kernel/syscalls/creat/creat05.c index bf409943..32074a44 100755 --- a/testcases/kernel/syscalls/creat/creat05.c +++ b/testcases/kernel/syscalls/creat/creat05.c @@ -74,6 +74,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .test_all = verify_creat, .needs_tmpdir = 1, .setup = setup, diff --git a/testcases/kernel/syscalls/creat/creat06.c b/testcases/kernel/syscalls/creat/creat06.c index 5c82e475..bd9835ea 100755 --- a/testcases/kernel/syscalls/creat/creat06.c +++ b/testcases/kernel/syscalls/creat/creat06.c @@ -60,9 +60,7 @@ static void setup(void); static void test6_setup(void); static void test6_cleanup(void); -#if !defined(UCLINUX) static void bad_addr_setup(int); -#endif static struct passwd *ltpuser; static char long_name[PATH_MAX+2]; @@ -78,9 +76,7 @@ static struct test_case_t { {long_name, MODE1, ENAMETOOLONG, NULL, NULL}, {NO_DIR, MODE1, ENOENT, NULL, NULL}, {NOT_DIR, MODE1, ENOTDIR, NULL, NULL}, -#if !defined(UCLINUX) {NULL, MODE1, EFAULT, bad_addr_setup, NULL}, -#endif {TEST6_FILE, MODE1, EACCES, test6_setup, test6_cleanup}, {TEST7_FILE, MODE1, ELOOP, NULL, NULL}, {TEST8_FILE, MODE1, EROFS, NULL, NULL}, @@ -127,7 +123,6 @@ static void setup(void) SAFE_SYMLINK("test_file_eloop2", TEST7_FILE); } -#if !defined(UCLINUX) static void bad_addr_setup(int i) { if (tcases[i].fname) @@ -136,7 +131,6 @@ static void bad_addr_setup(int i) tcases[i].fname = SAFE_MMAP(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); } -#endif static void test6_setup(void) { diff --git a/testcases/kernel/syscalls/creat/creat07.c b/testcases/kernel/syscalls/creat/creat07.c index 7bd32ab4..f157e1a8 100755 --- a/testcases/kernel/syscalls/creat/creat07.c +++ b/testcases/kernel/syscalls/creat/creat07.c @@ -47,7 +47,17 @@ static void verify_creat(void) SAFE_WAITPID(pid, NULL, 0); } +static void setup(void) +{ + if ((tst_kvercmp(6, 11, 0)) >= 0) { + tst_brk(TCONF, "Skipping test, write to executed file is " + "allowed since 6.11-rc1.\n" + "2a010c412853 (\"fs: don't block i_writecount during exec\")"); + } +} + static struct tst_test test = { + .setup = setup, .test_all = verify_creat, .needs_checkpoints = 1, .forks_child = 1, diff --git a/testcases/kernel/syscalls/creat/creat08.c b/testcases/kernel/syscalls/creat/creat08.c index 91581dbf..ab537de9 100755 --- a/testcases/kernel/syscalls/creat/creat08.c +++ b/testcases/kernel/syscalls/creat/creat08.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that the group ID and setgid bit are set correctly when a new file * is created. */ @@ -29,7 +27,6 @@ #define NOSETGID_B DIR_B "/nosetgid" #define ROOT_SETGID DIR_B "/root_setgid" -static char *tmpdir; static uid_t orig_uid, nobody_uid; static gid_t nobody_gid, free_gid; static int fd = -1; @@ -44,7 +41,6 @@ static void setup(void) tst_res(TINFO, "User nobody: uid = %d, gid = %d", (int)nobody_uid, (int)nobody_gid); free_gid = tst_get_free_gid(nobody_gid); - tmpdir = tst_get_tmpdir(); } static void file_test(const char *name, mode_t mode, int sgid, gid_t gid) @@ -125,15 +121,13 @@ static void run(void) file_test(ROOT_SETGID, MODE_SGID, 1, free_gid); /* Cleanup between loops */ - tst_purge_dir(tmpdir); + tst_purge_dir(tst_tmpdir_path()); } static void cleanup(void) { if (fd >= 0) SAFE_CLOSE(fd); - - free(tmpdir); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/creat/creat09.c b/testcases/kernel/syscalls/creat/creat09.c index a5d3740a..ac46c940 100755 --- a/testcases/kernel/syscalls/creat/creat09.c +++ b/testcases/kernel/syscalls/creat/creat09.c @@ -3,8 +3,6 @@ * Copyright (c) 2021 SUSE LLC */ /*\ - * [Description] - * * CVE-2018-13405 * * Check for possible privilege escalation through creating files with setgid @@ -138,6 +136,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .test = run, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/delete_module/.gitignore b/testcases/kernel/syscalls/delete_module/.gitignore index 1a774a6d..e08f20d1 100755 --- a/testcases/kernel/syscalls/delete_module/.gitignore +++ b/testcases/kernel/syscalls/delete_module/.gitignore @@ -3,10 +3,7 @@ /delete_module03 /*.ko /*.mod.c -/*.ko.cmd -/*.mod.cmd -/*.mod.o.cmd -/*.o.cmd -/.built-in.a.cmd +/*.cmd /Module.symvers /modules.order +modules.livepatch diff --git a/testcases/kernel/syscalls/delete_module/delete_module01.c b/testcases/kernel/syscalls/delete_module/delete_module01.c index 90d8b528..a64c1b63 100755 --- a/testcases/kernel/syscalls/delete_module/delete_module01.c +++ b/testcases/kernel/syscalls/delete_module/delete_module01.c @@ -7,13 +7,12 @@ */ /*\ - * [Description] - * * Basic test for delete_module(2). * * Install dummy_del_mod.ko and delete it with delete_module(2). */ +#include #include "tst_test.h" #include "tst_module.h" #include "lapi/syscalls.h" @@ -25,6 +24,8 @@ static int module_loaded; static void do_delete_module(void) { + tst_requires_module_signature_disabled(); + if (!module_loaded) { tst_module_load(MODULE_NAME_KO, NULL); module_loaded = 1; diff --git a/testcases/kernel/syscalls/delete_module/delete_module03.c b/testcases/kernel/syscalls/delete_module/delete_module03.c index 7e92fc2a..e36b8efe 100755 --- a/testcases/kernel/syscalls/delete_module/delete_module03.c +++ b/testcases/kernel/syscalls/delete_module/delete_module03.c @@ -12,6 +12,7 @@ * if tried to remove a module while other modules depend on this module. */ +#include #include #include "tst_test.h" #include "tst_module.h" @@ -50,6 +51,8 @@ static void do_delete_module(void) static void setup(void) { + tst_requires_module_signature_disabled(); + /* Load first kernel module */ tst_module_load(DUMMY_MOD_KO, NULL); dummy_mod_loaded = 1; diff --git a/testcases/kernel/syscalls/delete_module/dummy_del_mod.c b/testcases/kernel/syscalls/delete_module/dummy_del_mod.c index 0ca7bea3..4257bb50 100755 --- a/testcases/kernel/syscalls/delete_module/dummy_del_mod.c +++ b/testcases/kernel/syscalls/delete_module/dummy_del_mod.c @@ -14,6 +14,8 @@ #include #include +#define DIRNAME "dummy_delmod" + /* Dummy function called by dependent module */ int dummy_func_test(void) { @@ -25,13 +27,13 @@ static int __init dummy_init(void) { struct proc_dir_entry *proc_dummy; - proc_dummy = proc_mkdir("dummy", 0); + proc_dummy = proc_mkdir(DIRNAME, 0); return 0; } static void __exit dummy_exit(void) { - remove_proc_entry("dummy", 0); + remove_proc_entry(DIRNAME, 0); } module_init(dummy_init); diff --git a/testcases/kernel/syscalls/delete_module/dummy_del_mod_dep.c b/testcases/kernel/syscalls/delete_module/dummy_del_mod_dep.c index 85b32791..8c891cf4 100755 --- a/testcases/kernel/syscalls/delete_module/dummy_del_mod_dep.c +++ b/testcases/kernel/syscalls/delete_module/dummy_del_mod_dep.c @@ -16,20 +16,22 @@ #include #include +#define DIRNAME "dummy_dep" + extern int dummy_func_test(void); static int __init dummy_init(void) { struct proc_dir_entry *proc_dummy; - proc_dummy = proc_mkdir("dummy_dep", 0); + proc_dummy = proc_mkdir(DIRNAME, 0); dummy_func_test(); return 0; } static void __exit dummy_exit(void) { - remove_proc_entry("dummy_dep", 0); + remove_proc_entry(DIRNAME, 0); } module_init(dummy_init); diff --git a/testcases/kernel/syscalls/dup/dup01.c b/testcases/kernel/syscalls/dup/dup01.c index f5cd058e..7a1da272 100755 --- a/testcases/kernel/syscalls/dup/dup01.c +++ b/testcases/kernel/syscalls/dup/dup01.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Verify that dup(2) syscall executes successfully and allocates * a new file descriptor which refers to the same open file as oldfd. */ diff --git a/testcases/kernel/syscalls/dup/dup02.c b/testcases/kernel/syscalls/dup/dup02.c index 5391738a..b6e270ab 100755 --- a/testcases/kernel/syscalls/dup/dup02.c +++ b/testcases/kernel/syscalls/dup/dup02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that dup(2) syscall fails with errno EBADF when called with * invalid value for oldfd argument. */ diff --git a/testcases/kernel/syscalls/dup/dup03.c b/testcases/kernel/syscalls/dup/dup03.c index d59e61f2..1d9919eb 100755 --- a/testcases/kernel/syscalls/dup/dup03.c +++ b/testcases/kernel/syscalls/dup/dup03.c @@ -5,8 +5,6 @@ * */ /*\ - * [Description] - * * Verify that dup(2) syscall fails with errno EMFILE when the per-process * limit on the number of open file descriptors has been reached. */ diff --git a/testcases/kernel/syscalls/dup/dup04.c b/testcases/kernel/syscalls/dup/dup04.c index 053fb40c..4869f437 100755 --- a/testcases/kernel/syscalls/dup/dup04.c +++ b/testcases/kernel/syscalls/dup/dup04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for dup(2) of a system pipe descriptor. */ diff --git a/testcases/kernel/syscalls/dup/dup05.c b/testcases/kernel/syscalls/dup/dup05.c index 619b4861..e2bc9a4a 100755 --- a/testcases/kernel/syscalls/dup/dup05.c +++ b/testcases/kernel/syscalls/dup/dup05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for dup(2) of a named pipe descriptor. */ diff --git a/testcases/kernel/syscalls/dup/dup06.c b/testcases/kernel/syscalls/dup/dup06.c index e3f8070b..accc32d6 100755 --- a/testcases/kernel/syscalls/dup/dup06.c +++ b/testcases/kernel/syscalls/dup/dup06.c @@ -1,41 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2002 - * ported from SPIE, section2/iosuite/dup1.c, by Airong Zhang - * Copyright (c) 2013 Cyril Hrubis - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * Ported from SPIE, section2/iosuite/dup1.c, by Airong Zhang + * Copyright (c) 2013 Cyril Hrubis + * Copyright (c) Linux Test Project, 2003-2024 */ -/* - WHAT: Does dup return -1 on the 21st file? - HOW: Create up to _NFILE (20) files and check for -1 return on the - next attempt - Should check NOFILE as well as _NFILE. 19-Jun-84 Dale. -*/ +/*\ + * Test for dup(2) syscall with max open file descriptors. + */ -#include -#include -#include -#include -#include -#include -#include -#include "test.h" +#include +#include "tst_test.h" -char *TCID = "dup06"; -int TST_TOTAL = 1; +static int *pfildes; +static int minfd, maxfd, freefds; +static char pfilname[40]; static int cnt_free_fds(int maxfd) { @@ -45,70 +25,53 @@ static int cnt_free_fds(int maxfd) if (fcntl(maxfd, F_GETFD) == -1 && errno == EBADF) freefds++; - return (freefds); -} - -static void setup(void); -static void cleanup(void); - -int main(int ac, char **av) -{ - int *fildes, i; - int min; - int freefds; - int lc; - const char *pfilname = "dup06"; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - min = getdtablesize(); - freefds = cnt_free_fds(min); - fildes = malloc((min + 5) * sizeof(int)); - - for (i = 0; i < min + 5; i++) - fildes[i] = 0; - - for (lc = 0; TEST_LOOPING(lc); lc++) { - unlink(pfilname); - - if ((fildes[0] = creat(pfilname, 0666)) == -1) { - tst_resm(TFAIL, "Cannot open first file"); - } else { - for (i = 1; i < min + 5; i++) { - if ((fildes[i] = dup(fildes[i - 1])) == -1) - break; - } - if (i < freefds) { - tst_resm(TFAIL, "Not enough files duped"); - } else if (i > freefds) { - tst_resm(TFAIL, "Too many files duped"); - } else { - tst_resm(TPASS, "Test passed."); - } - } - - unlink(pfilname); - - for (i = 0; i < min + 5; i++) { - if (fildes[i] != 0 && fildes[i] != -1) - close(fildes[i]); - - fildes[i] = 0; - } - } - - cleanup(); - tst_exit(); + return freefds; } static void setup(void) { - tst_tmpdir(); + minfd = getdtablesize(); /* get number of files allowed open */ + maxfd = minfd + 5; + freefds = cnt_free_fds(minfd); + pfildes = SAFE_MALLOC(maxfd * sizeof(int)); + memset(pfildes, -1, maxfd * sizeof(int)); + sprintf(pfilname, "./dup06.%d\n", getpid()); } static void cleanup(void) { - tst_rmdir(); + if (pfildes != NULL) + free(pfildes); } + +static void run(void) +{ + int i; + + pfildes[0] = SAFE_CREAT(pfilname, 0666); + for (i = 1; i < maxfd; i++) { + pfildes[i] = dup(pfildes[i - 1]); + if (pfildes[i] == -1) + break; + } + if (i < freefds) + tst_res(TFAIL, "Not enough files duped"); + else if (i > freefds) + tst_res(TFAIL, "Too many files duped"); + else + tst_res(TPASS, "Test passed"); + + SAFE_UNLINK(pfilname); + + for (i = 0; i < maxfd; i++) { + if (pfildes[i] != 0 && pfildes[i] != -1) + SAFE_CLOSE(pfildes[i]); + } +} + +static struct tst_test test = { + .needs_tmpdir = 1, + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/dup/dup07.c b/testcases/kernel/syscalls/dup/dup07.c index a100f5d5..aa91d5d5 100755 --- a/testcases/kernel/syscalls/dup/dup07.c +++ b/testcases/kernel/syscalls/dup/dup07.c @@ -1,142 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2002 - * ported from SPIE, section2/iosuite/dup3.c, by Airong Zhang - * Copyright (c) 2013 Cyril Hrubis - * - * 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 + * Copyright (c) International Business Machines Corp., 2002 + * Ported from SPIE, section2/iosuite/dup3.c, by Airong Zhang + * Copyright (c) 2013 Cyril Hrubis + * Copyright (c) Linux Test Project, 2006-2024 */ -/* - WHAT: Is the access mode the same for both file descriptors? - 0: read only? - 1: write only? - 2: read/write? - HOW: Creat a file with each access mode; dup each file descriptor; - stat each file descriptor and compare mode of each pair -*/ +/*\ + * Verify that the file descriptor created by dup(2) syscall has the same + * access mode as the old one. + */ -#include -#include -#include -#include -#include -#include -#include "test.h" - -char *TCID = "dup07"; -int TST_TOTAL = 3; +#include "tst_test.h" static const char *testfile = "dup07"; -static void setup(void); -static void cleanup(void); +static struct tcase { + char *mode_desc; + int mode; +} tcases[] = { + {"read only", 0444}, + {"write only", 0222}, + {"read/write", 0666}, +}; -int main(int ac, char **av) +static void run(unsigned int n) { - struct stat retbuf; - struct stat dupbuf; - int rdoret, wroret, rdwret; - int duprdo, dupwro, duprdwr; + int oldfd, dupfd; + struct stat oldbuf, dupbuf; + struct tcase *tc = &tcases[n]; - int lc; + oldfd = SAFE_CREAT(testfile, tc->mode); + dupfd = TST_EXP_FD_SILENT(dup(oldfd), "dup() %s file", tc->mode_desc); + if (TST_PASS) { + SAFE_FSTAT(oldfd, &oldbuf); + SAFE_FSTAT(dupfd, &dupbuf); - tst_parse_opts(ac, av, NULL, NULL); + if (oldbuf.st_mode != dupbuf.st_mode) + tst_res(TFAIL, "%s and dup do not match", tc->mode_desc); + else + tst_res(TPASS, "Passed in %s mode", tc->mode_desc); - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - if ((rdoret = creat(testfile, 0444)) == -1) { - tst_resm(TFAIL, "Unable to creat file '%s'", testfile); - } else { - if ((duprdo = dup(rdoret)) == -1) { - tst_resm(TFAIL, "Unable to dup '%s'", testfile); - } else { - fstat(rdoret, &retbuf); - fstat(duprdo, &dupbuf); - if (retbuf.st_mode != dupbuf.st_mode) { - tst_resm(TFAIL, - "rdonly and dup do not match"); - } else { - tst_resm(TPASS, - "Passed in read mode."); - } - close(duprdo); - } - close(rdoret); - } - - unlink(testfile); - - if ((wroret = creat(testfile, 0222)) == -1) { - tst_resm(TFAIL, "Unable to creat file '%s'", testfile); - } else { - if ((dupwro = dup(wroret)) == -1) { - tst_resm(TFAIL, "Unable to dup '%s'", testfile); - } else { - fstat(wroret, &retbuf); - fstat(dupwro, &dupbuf); - if (retbuf.st_mode != dupbuf.st_mode) { - tst_resm(TFAIL, - "wronly and dup do not match"); - } else { - tst_resm(TPASS, - "Passed in write mode."); - } - close(dupwro); - } - close(wroret); - - } - - unlink(testfile); - - if ((rdwret = creat(testfile, 0666)) == -1) { - tst_resm(TFAIL, "Unable to creat file '%s'", testfile); - } else { - if ((duprdwr = dup(rdwret)) == -1) { - tst_resm(TFAIL, "Unable to dup '%s'", testfile); - } else { - fstat(rdwret, &retbuf); - fstat(duprdwr, &dupbuf); - if (retbuf.st_mode != dupbuf.st_mode) { - tst_resm(TFAIL, - "rdwr and dup do not match"); - } else { - tst_resm(TPASS, - "Passed in read/write mode."); - } - close(duprdwr); - } - close(rdwret); - } - - unlink(testfile); + SAFE_CLOSE(dupfd); } - cleanup(); - tst_exit(); + SAFE_CLOSE(oldfd); + SAFE_UNLINK(testfile); } -static void setup(void) -{ - tst_tmpdir(); -} - -static void cleanup(void) -{ - tst_rmdir(); -} +static struct tst_test test = { + .needs_tmpdir = 1, + .test = run, + .tcnt = ARRAY_SIZE(tcases), +}; diff --git a/testcases/kernel/syscalls/dup2/dup201.c b/testcases/kernel/syscalls/dup2/dup201.c index d851eea7..9d2c7eb3 100755 --- a/testcases/kernel/syscalls/dup2/dup201.c +++ b/testcases/kernel/syscalls/dup2/dup201.c @@ -5,8 +5,6 @@ * 01/2002 Removed EMFILE test - Paul Larson */ /*\ - * [Description] - * * Negative tests for dup2() with bad fd (EBADF). * * - First fd argument is less than 0 diff --git a/testcases/kernel/syscalls/dup2/dup202.c b/testcases/kernel/syscalls/dup2/dup202.c index 659f3a4e..b4dcd195 100755 --- a/testcases/kernel/syscalls/dup2/dup202.c +++ b/testcases/kernel/syscalls/dup2/dup202.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test whether the access mode are the same for both file descriptors. * * Create file with mode, dup2, [change mode], check mode diff --git a/testcases/kernel/syscalls/dup2/dup203.c b/testcases/kernel/syscalls/dup2/dup203.c index c8d1095f..384662ee 100755 --- a/testcases/kernel/syscalls/dup2/dup203.c +++ b/testcases/kernel/syscalls/dup2/dup203.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Testcase to check the basic functionality of dup2(). * * - Attempt to dup2() on an open file descriptor. diff --git a/testcases/kernel/syscalls/dup2/dup204.c b/testcases/kernel/syscalls/dup2/dup204.c index 112ce0c9..dbb0233a 100755 --- a/testcases/kernel/syscalls/dup2/dup204.c +++ b/testcases/kernel/syscalls/dup2/dup204.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test whether the inode number are the same for both file descriptors. */ diff --git a/testcases/kernel/syscalls/dup2/dup205.c b/testcases/kernel/syscalls/dup2/dup205.c index fa7b27d9..b95a9212 100755 --- a/testcases/kernel/syscalls/dup2/dup205.c +++ b/testcases/kernel/syscalls/dup2/dup205.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Negative test for dup2() with max open file descriptors. */ diff --git a/testcases/kernel/syscalls/dup2/dup206.c b/testcases/kernel/syscalls/dup2/dup206.c index 17d527a1..549d3a9f 100755 --- a/testcases/kernel/syscalls/dup2/dup206.c +++ b/testcases/kernel/syscalls/dup2/dup206.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * If oldfd is a valid file descriptor, and newfd has the same value as oldfd, * then dup2() does nothing, and returns newfd. */ diff --git a/testcases/kernel/syscalls/dup2/dup207.c b/testcases/kernel/syscalls/dup2/dup207.c index f1b184f5..c6c6cb48 100755 --- a/testcases/kernel/syscalls/dup2/dup207.c +++ b/testcases/kernel/syscalls/dup2/dup207.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test whether the file offset are the same for both file descriptors. */ diff --git a/testcases/kernel/syscalls/dup3/dup3_01.c b/testcases/kernel/syscalls/dup3/dup3_01.c index 517491d4..3f27b1c7 100755 --- a/testcases/kernel/syscalls/dup3/dup3_01.c +++ b/testcases/kernel/syscalls/dup3/dup3_01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Testcase to check whether dup3() supports O_CLOEXEC flag. */ diff --git a/testcases/kernel/syscalls/dup3/dup3_02.c b/testcases/kernel/syscalls/dup3/dup3_02.c index 009b0037..a618b3e9 100755 --- a/testcases/kernel/syscalls/dup3/dup3_02.c +++ b/testcases/kernel/syscalls/dup3/dup3_02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test for various EINVAL error. * * - oldfd is equal to newfd without using O_CLOEXEC flag diff --git a/testcases/kernel/syscalls/epoll/epoll-ltp.c b/testcases/kernel/syscalls/epoll/epoll-ltp.c index 8b04f532..dac132e2 100755 --- a/testcases/kernel/syscalls/epoll/epoll-ltp.c +++ b/testcases/kernel/syscalls/epoll/epoll-ltp.c @@ -143,7 +143,7 @@ do { \ int kid_status; \ \ tst_old_flush(); \ - kid_pid = FORK_OR_VFORK(); \ + kid_pid = tst_fork(); \ if (kid_pid == 0) { #define PROTECT_REGION_EXIT(errval) return (errval); @@ -177,7 +177,7 @@ do { \ int kid_status; \ \ tst_old_flush(); \ - kid_pid = FORK_OR_VFORK(); \ + kid_pid = tst_fork(); \ if (kid_pid == 0) { /* Run the function */ \ return fn(epoll_fd); \ } else { \ diff --git a/testcases/kernel/syscalls/epoll_create/epoll_create01.c b/testcases/kernel/syscalls/epoll_create/epoll_create01.c index 443554fe..2e8e813d 100755 --- a/testcases/kernel/syscalls/epoll_create/epoll_create01.c +++ b/testcases/kernel/syscalls/epoll_create/epoll_create01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that epoll_create return a nonnegative file descriptor on success. * * The size argument informed the kernel of the number of file descriptors diff --git a/testcases/kernel/syscalls/epoll_create/epoll_create02.c b/testcases/kernel/syscalls/epoll_create/epoll_create02.c index 942d7afd..2ec8e925 100755 --- a/testcases/kernel/syscalls/epoll_create/epoll_create02.c +++ b/testcases/kernel/syscalls/epoll_create/epoll_create02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that epoll_create returns -1 and set errno to EINVAL if size is not * greater than zero. */ diff --git a/testcases/kernel/syscalls/epoll_create1/epoll_create1_01.c b/testcases/kernel/syscalls/epoll_create1/epoll_create1_01.c index 6d2bf2a3..9e43f4d4 100755 --- a/testcases/kernel/syscalls/epoll_create1/epoll_create1_01.c +++ b/testcases/kernel/syscalls/epoll_create1/epoll_create1_01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that epoll_create1 sets the close-on-exec flag for the returned * file descriptor with EPOLL_CLOEXEC. */ diff --git a/testcases/kernel/syscalls/epoll_create1/epoll_create1_02.c b/testcases/kernel/syscalls/epoll_create1/epoll_create1_02.c index 74c31169..3a2ac988 100755 --- a/testcases/kernel/syscalls/epoll_create1/epoll_create1_02.c +++ b/testcases/kernel/syscalls/epoll_create1/epoll_create1_02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that epoll_create1 returns -1 and set errno to EINVAL with an invalid * value specified in flags. */ diff --git a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl01.c b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl01.c index 298ed89c..25752d8e 100755 --- a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl01.c +++ b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl01.c @@ -5,16 +5,14 @@ */ /*\ - * [Description] - * * Check the basic functionality of the epoll_ctl: * * - When epoll_ctl succeeds to register fd on the epoll instance and associates - * event with fd, epoll_wait will get registered fd and event correctly. + * event with fd, epoll_wait will get registered fd and event correctly. * - When epoll_ctl succeeds to change event which is related to fd, epoll_wait - * will get changed event correctly. + * will get changed event correctly. * - When epoll_ctl succeeds to deregister fd from the epoll instance epoll_wait - * won't get deregistered fd and event. + * won't get deregistered fd and event. */ #include diff --git a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl02.c b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl02.c index dcf74bf7..a17148f1 100755 --- a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl02.c +++ b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that epoll_ctl() fails with: * * - EBADF if epfd is an invalid fd. diff --git a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl03.c b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl03.c index f617295c..e8d3d5f3 100755 --- a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl03.c +++ b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Check that epoll_ctl returns zero with different combinations of events on * success. */ diff --git a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl04.c b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl04.c index bc015c01..feb49623 100755 --- a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl04.c +++ b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that the maximum number of nesting allowed inside epoll sets is 5, * otherwise epoll_ctl fails with EINVAL. */ @@ -53,13 +51,16 @@ static void cleanup(void) static void verify_epoll_ctl(void) { + const int exp_errnos[] = {EINVAL, ELOOP}; + new_epfd = epoll_create(1); if (new_epfd == -1) tst_brk(TBROK | TERRNO, "fail to create epoll instance"); events.data.fd = epfd; - TST_EXP_FAIL(epoll_ctl(new_epfd, EPOLL_CTL_ADD, epfd, &events), EINVAL, - "epoll_ctl(..., EPOLL_CTL_ADD, ...) with number of nesting is 5"); + TST_EXP_FAIL2_ARR(epoll_ctl(new_epfd, EPOLL_CTL_ADD, epfd, &events), + exp_errnos, ARRAY_SIZE(exp_errnos), + "epoll_ctl(..., EPOLL_CTL_ADD, ...) with number of nesting is 5"); SAFE_CLOSE(new_epfd); } diff --git a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl05.c b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl05.c index 71e300da..fc59ef25 100755 --- a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl05.c +++ b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that epoll_ctl() fails with ELOOP if fd refers to an epoll instance * and this EPOLL_CTL_ADD operation would result in a circular loop of epoll * instances monitoring one another. diff --git a/testcases/kernel/syscalls/epoll_pwait/.gitignore b/testcases/kernel/syscalls/epoll_pwait/.gitignore index fafb2d78..81e77b8d 100755 --- a/testcases/kernel/syscalls/epoll_pwait/.gitignore +++ b/testcases/kernel/syscalls/epoll_pwait/.gitignore @@ -3,3 +3,4 @@ epoll_pwait02 epoll_pwait03 epoll_pwait04 epoll_pwait05 +epoll_pwait06 diff --git a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait01.c b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait01.c index f3ca894f..012ba156 100755 --- a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait01.c +++ b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for epoll_pwait() and epoll_pwait2(). * * - With a sigmask a signal is ignored and the syscall safely waits until @@ -32,7 +30,7 @@ static void sighandler(int sig LTP_ATTRIBUTE_UNUSED) {} static void verify_sigmask(void) { - TEST(do_epoll_pwait(efd, &e, 1, -1, &signalset)); + TEST(do_epoll_pwait(efd, &e, 1, NULL, &signalset)); if (TST_RET != 1) { tst_res(TFAIL, "do_epoll_pwait() returned %li, expected 1", @@ -45,7 +43,7 @@ static void verify_sigmask(void) static void verify_nonsigmask(void) { - TST_EXP_FAIL(do_epoll_pwait(efd, &e, 1, -1, NULL), EINTR, + TST_EXP_FAIL(do_epoll_pwait(efd, &e, 1, NULL, NULL), EINTR, "do_epoll_pwait() without sigmask"); } diff --git a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait02.c b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait02.c index 3341a2b0..a27c7db9 100755 --- a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait02.c +++ b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic test for epoll_pwait and epoll_pwait2. Checks if data avaiable in a * file descriptor are reported correctly in the syscall return value. */ @@ -21,7 +19,7 @@ static struct epoll_event e; static void run(void) { - TEST(do_epoll_pwait(efd, &e, 1, -1, NULL)); + TEST(do_epoll_pwait(efd, &e, 1, NULL, NULL)); if (TST_RET == 1) { tst_res(TPASS, "do_epoll_pwait() succeeded"); diff --git a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait03.c b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait03.c index 94b785b1..572d347f 100755 --- a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait03.c +++ b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Check that epoll_pwait and epoll_pwait2 timeouts correctly. */ @@ -15,17 +13,20 @@ #include "tst_timer_test.h" #include "epoll_pwait_var.h" -#define USEC_PER_MSEC (1000L) +#define USEC_PER_NSEC (1000L) +#define USEC_PER_SEC (1000000L) static int efd, sfd[2]; static struct epoll_event e; int sample_fn(int clk_id, long long usec) { - unsigned int ms = usec / USEC_PER_MSEC; + struct timespec ts; + ts.tv_sec = usec / USEC_PER_SEC; + ts.tv_nsec = (usec % USEC_PER_SEC) * USEC_PER_NSEC; tst_timer_start(clk_id); - TEST(do_epoll_pwait(efd, &e, 1, ms, NULL)); + TEST(do_epoll_pwait(efd, &e, 1, &ts, NULL)); tst_timer_stop(); tst_timer_sample(); diff --git a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait04.c b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait04.c index a75751db..b399225b 100755 --- a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait04.c +++ b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that, epoll_pwait() and epoll_pwait2() return -1 and set errno to * EFAULT with a sigmask points outside user's accessible address space. */ @@ -22,7 +20,7 @@ static void *bad_addr; static void run(void) { - TST_EXP_FAIL(do_epoll_pwait(efd, &e, 1, -1, bad_addr), + TST_EXP_FAIL(do_epoll_pwait(efd, &e, 1, NULL, bad_addr), EFAULT, "with an invalid sigmask pointer"); } diff --git a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait05.c b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait05.c index b75a9953..9ac5e826 100755 --- a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait05.c +++ b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that, epoll_pwait2() return -1 and set errno to EINVAL with an * invalid timespec. */ diff --git a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait06.c b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait06.c new file mode 100644 index 00000000..d47327be --- /dev/null +++ b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait06.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC + */ + +/*\ + * Verify that various timeout values don't get misinterpreted as infinity + * by epoll_pwait() and epoll_pwait2(). Regression fixed in: + * + * commit d9ec73301099ec5975505e1c3effbe768bab9490 + * Author: Max Kellermann + * Date: Tue Apr 29 20:58:27 2025 +0200 + * + * fs/eventpoll: fix endless busy loop after timeout has expired + */ + +#include "tst_test.h" +#include "tst_timer.h" +#include "tst_epoll.h" +#include "epoll_pwait_var.h" + +static int efd = -1; + +static void run(void) +{ + struct timespec timeout = {}; + struct epoll_event e = {}; + + e.events = EPOLLIN; + + TST_FD_FOREACH(fd_in) { + /* File descriptor types not supported by epoll */ + switch (fd_in.type) { + case TST_FD_FILE: + case TST_FD_PATH: + case TST_FD_DIR: + case TST_FD_DEV_ZERO: + case TST_FD_PROC_MAPS: + case TST_FD_BPF_MAP: + case TST_FD_FSOPEN: + case TST_FD_FSPICK: + case TST_FD_OPEN_TREE: + case TST_FD_MEMFD: + case TST_FD_MEMFD_SECRET: + continue; + default: + break; + } + + tst_res(TINFO, "Testing %s", tst_fd_desc(&fd_in)); + timeout.tv_nsec = 1000000000; + SAFE_EPOLL_CTL(efd, EPOLL_CTL_ADD, fd_in.fd, &e); + + do { + alarm(1); + timeout.tv_nsec /= 10; + do_epoll_pwait(efd, &e, 1, &timeout, NULL); + alarm(0); + } while (timeout.tv_nsec); + + SAFE_EPOLL_CTL(efd, EPOLL_CTL_DEL, fd_in.fd, &e); + } + + tst_res(TPASS, "Timeout works correctly"); +} + +static void setup(void) +{ + epoll_pwait_init(); + efd = SAFE_EPOLL_CREATE1(0); +} + +static void cleanup(void) +{ + if (efd >= 0) + SAFE_CLOSE(efd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .test_variants = TEST_VARIANTS, + .tags = (const struct tst_tag[]) { + {"linux-git", "d9ec73301099"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait_var.h b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait_var.h index 58a3f15a..e215a654 100755 --- a/testcases/kernel/syscalls/epoll_pwait/epoll_pwait_var.h +++ b/testcases/kernel/syscalls/epoll_pwait/epoll_pwait_var.h @@ -14,22 +14,20 @@ #define NSEC_PER_MSEC (1000000L) static int do_epoll_pwait(int epfd, struct epoll_event *events, int - maxevents, int timeout, const sigset_t *sigmask) + maxevents, struct timespec *timeout, const sigset_t *sigmask) { - if (tst_variant == 0) - return epoll_pwait(epfd, events, maxevents, timeout, sigmask); + if (tst_variant == 1) + return epoll_pwait2(epfd, events, maxevents, timeout, sigmask); - struct timespec ts; + int timeout_ms = -1; - if (timeout < 0) { - return epoll_pwait2(epfd, events, maxevents, NULL, sigmask); - } else { - ts.tv_sec = timeout / MSEC_PER_SEC; - ts.tv_nsec = NSEC_PER_MSEC * (timeout % MSEC_PER_SEC); + if (timeout) { + timeout_ms = timeout->tv_sec * MSEC_PER_SEC; + timeout_ms += (timeout->tv_nsec + NSEC_PER_MSEC - 1) / + NSEC_PER_MSEC; } - return epoll_pwait2(epfd, events, maxevents, &ts, sigmask); - + return epoll_pwait(epfd, events, maxevents, timeout_ms, sigmask); } static void epoll_pwait_init(void) diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait01.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait01.c index 4f843848..2485be51 100755 --- a/testcases/kernel/syscalls/epoll_wait/epoll_wait01.c +++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic test for epoll_wait. Check that epoll_wait works for EPOLLOUT and * EPOLLIN events on an epoll instance and that struct epoll_event is set * correctly. diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait02.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait02.c index 93ada1cf..9b6e932b 100755 --- a/testcases/kernel/syscalls/epoll_wait/epoll_wait02.c +++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that epoll_wait(2) timeouts correctly. */ diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait03.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait03.c index d31e4986..e55d65ca 100755 --- a/testcases/kernel/syscalls/epoll_wait/epoll_wait03.c +++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for epoll_wait: * * - epoll_wait fails with EBADF if epfd is not a valid file descriptor. diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait04.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait04.c index bd8baca2..5c4a5454 100755 --- a/testcases/kernel/syscalls/epoll_wait/epoll_wait04.c +++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Check that a timeout equal to zero causes epoll_wait() to return immediately. */ diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait05.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait05.c index d06a024f..33d08c0c 100644 --- a/testcases/kernel/syscalls/epoll_wait/epoll_wait05.c +++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait05.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that epoll receives EPOLLRDHUP event when we hang a reading * half-socket we are polling on. */ diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait06.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait06.c index f35e0423..0bf8a29d 100644 --- a/testcases/kernel/syscalls/epoll_wait/epoll_wait06.c +++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait06.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that edge triggering is correctly handled by epoll, for both EPOLLIN * and EPOLLOUT. * diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait07.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait07.c index dfabd0d8..329bf2a5 100644 --- a/testcases/kernel/syscalls/epoll_wait/epoll_wait07.c +++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait07.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that EPOLLONESHOT is correctly handled by epoll_wait. * We open a channel, write in it two times and verify that EPOLLIN has been * received only once. diff --git a/testcases/kernel/syscalls/eventfd/eventfd01.c b/testcases/kernel/syscalls/eventfd/eventfd01.c index b66d6a55..4fe209a1 100755 --- a/testcases/kernel/syscalls/eventfd/eventfd01.c +++ b/testcases/kernel/syscalls/eventfd/eventfd01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify read operation for eventfd fail with: * * - EAGAIN when counter is zero on non blocking fd diff --git a/testcases/kernel/syscalls/eventfd/eventfd02.c b/testcases/kernel/syscalls/eventfd/eventfd02.c index f028961c..420df67a 100644 --- a/testcases/kernel/syscalls/eventfd/eventfd02.c +++ b/testcases/kernel/syscalls/eventfd/eventfd02.c @@ -7,13 +7,11 @@ */ /*\ - * [Description] - * * Verify write operation for eventfd fail with: * * - EAGAIN when counter is zero on non blocking fd * - EINVAL when buffer size is less than 8 bytes, or if an attempt is made to - * write the value 0xffffffffffffffff + * write the value 0xffffffffffffffff */ #include diff --git a/testcases/kernel/syscalls/eventfd/eventfd03.c b/testcases/kernel/syscalls/eventfd/eventfd03.c index 452264ca..31761a44 100644 --- a/testcases/kernel/syscalls/eventfd/eventfd03.c +++ b/testcases/kernel/syscalls/eventfd/eventfd03.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test whether readfd is set by select() when eventfd() counter value is * non-zero, then check if readfd is not set when eventfd() counter value is * zero. diff --git a/testcases/kernel/syscalls/eventfd/eventfd04.c b/testcases/kernel/syscalls/eventfd/eventfd04.c index c7186ae6..4ee67d30 100644 --- a/testcases/kernel/syscalls/eventfd/eventfd04.c +++ b/testcases/kernel/syscalls/eventfd/eventfd04.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test whether writefd is set by select() when eventfd() counter value is * not the maximum value, then check if writefd is not set when eventfd() * counter value is maximum value. diff --git a/testcases/kernel/syscalls/eventfd/eventfd05.c b/testcases/kernel/syscalls/eventfd/eventfd05.c index 6ea2c413..76806c6b 100644 --- a/testcases/kernel/syscalls/eventfd/eventfd05.c +++ b/testcases/kernel/syscalls/eventfd/eventfd05.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test whether eventfd() counter update in child is reflected in the parent. */ diff --git a/testcases/kernel/syscalls/eventfd/eventfd06.c b/testcases/kernel/syscalls/eventfd/eventfd06.c index 7339dd47..0ac48b2c 100644 --- a/testcases/kernel/syscalls/eventfd/eventfd06.c +++ b/testcases/kernel/syscalls/eventfd/eventfd06.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test whether counter overflow is detected and handled correctly. * * It is not possible to directly overflow the counter using the @@ -136,6 +134,8 @@ static void test_poll(void) static void setup(void) { TEST(io_setup(MAXEVENTS, &ctx)); + if (TST_RET == -ENOSYS) + tst_brk(TCONF | TRERRNO, "io_setup(): AIO not supported by kernel"); if (TST_RET < 0) tst_brk(TBROK, "io_setup() failed: %s", tst_strerrno(-TST_RET)); diff --git a/testcases/kernel/syscalls/eventfd2/eventfd2.h b/testcases/kernel/syscalls/eventfd2/eventfd2.h index 5350820b..2333d1e5 100644 --- a/testcases/kernel/syscalls/eventfd2/eventfd2.h +++ b/testcases/kernel/syscalls/eventfd2/eventfd2.h @@ -3,6 +3,9 @@ * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ +#ifndef EVENTFD2_H__ +#define EVENTFD2_H__ + #include "tst_test.h" #include "lapi/syscalls.h" @@ -16,3 +19,5 @@ static inline int eventfd2(unsigned int count, unsigned int flags) return ret; } + +#endif /* EVENTFD2_H__ */ diff --git a/testcases/kernel/syscalls/eventfd2/eventfd2_01.c b/testcases/kernel/syscalls/eventfd2/eventfd2_01.c index 3a80379a..9c63891c 100755 --- a/testcases/kernel/syscalls/eventfd2/eventfd2_01.c +++ b/testcases/kernel/syscalls/eventfd2/eventfd2_01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test verifies that eventfd2 correctly set FD_CLOEXEC flag on file when * EFD_CLOEXEC flag is used. */ diff --git a/testcases/kernel/syscalls/eventfd2/eventfd2_02.c b/testcases/kernel/syscalls/eventfd2/eventfd2_02.c index 430cdb33..4d837804 100755 --- a/testcases/kernel/syscalls/eventfd2/eventfd2_02.c +++ b/testcases/kernel/syscalls/eventfd2/eventfd2_02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test verifies that eventfd2 correctly set O_NONBLOCK flag on file when * EFD_NONBLOCK flag is used. */ diff --git a/testcases/kernel/syscalls/eventfd2/eventfd2_03.c b/testcases/kernel/syscalls/eventfd2/eventfd2_03.c index e1949fd3..058e5734 100755 --- a/testcases/kernel/syscalls/eventfd2/eventfd2_03.c +++ b/testcases/kernel/syscalls/eventfd2/eventfd2_03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test verifies that eventfd2 semaphore-like support is properly working. */ diff --git a/testcases/kernel/syscalls/execl/execl01.c b/testcases/kernel/syscalls/execl/execl01.c index 9268d497..dfefd1fa 100755 --- a/testcases/kernel/syscalls/execl/execl01.c +++ b/testcases/kernel/syscalls/execl/execl01.c @@ -35,4 +35,8 @@ static struct tst_test test = { .forks_child = 1, .child_needs_reinit = 1, .test_all = verify_execl, + .ulimit = (const struct tst_ulimit_val[]) { + {RLIMIT_STACK, RLIM_INFINITY}, + {} + }, }; diff --git a/testcases/kernel/syscalls/execve/execve04.c b/testcases/kernel/syscalls/execve/execve04.c index 18e883ab..7b475dd1 100755 --- a/testcases/kernel/syscalls/execve/execve04.c +++ b/testcases/kernel/syscalls/execve/execve04.c @@ -8,7 +8,7 @@ * 04/2008 Roy Lee */ -/* +/*\ * Attempt to execve(2) a file which is being opened by another process for * writing fails with ETXTBSY. */ @@ -63,7 +63,17 @@ static void do_child(void) exit(0); } +static void setup(void) +{ + if ((tst_kvercmp(6, 11, 0)) >= 0) { + tst_brk(TCONF, "Skipping test, write to executed file is " + "allowed since 6.11-rc1.\n" + "2a010c412853 (\"fs: don't block i_writecount during exec\")"); + } +} + static struct tst_test test = { + .setup = setup, .test_all = verify_execve, .forks_child = 1, .child_needs_reinit = 1, diff --git a/testcases/kernel/syscalls/execve/execve05.c b/testcases/kernel/syscalls/execve/execve05.c index 87565d99..ec0fc102 100755 --- a/testcases/kernel/syscalls/execve/execve05.c +++ b/testcases/kernel/syscalls/execve/execve05.c @@ -1,32 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2018 Linux Test Project + * Copyright (c) 2001-2023 Linux Test Project * Copyright (c) International Business Machines Corp., 2001 - * + * Copyright (c) 2008 Renaud Lottiaux * Ported to LTP: Wayne Boyer - * 21/04/2008 Renaud Lottiaux (Renaud.Lottiaux@kerlabs.com) */ -/* - * NAME - * execve05.c - * - * DESCRIPTION - * This testcase tests the basic functionality of the execve(2) system - * call. - * - * ALGORITHM - * This tests the functionality of the execve(2) system call by spawning - * a few children, each of which would execute "execve_child" simultaneously, - * and finally the parent ensures that they terminated correctly. - * - * USAGE - * execve05 -i 5 -n 20 +/*\ + * This tests the functionality of the execve(2) system call by spawning + * a few children, each of which would execute "execve_child" simultaneously, + * and finally the parent ensures that they terminated correctly. */ -#ifndef _GNU_SOURCE #define _GNU_SOURCE -#endif + #include #include #include @@ -73,6 +60,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 3, .test_all = verify_execve, .options = (struct tst_option[]) { {"n:", &opt_nchild, "Numbers of children"}, diff --git a/testcases/kernel/syscalls/execve/execve06.c b/testcases/kernel/syscalls/execve/execve06.c index a0008926..f0ce990d 100644 --- a/testcases/kernel/syscalls/execve/execve06.c +++ b/testcases/kernel/syscalls/execve/execve06.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test that kernel adds dummy argv[0] if empty argument list was passed to * execve(). This fixes at least one CVE where userspace programs start to * process argument list blindly from argv[1] such as polkit pkexec diff --git a/testcases/kernel/syscalls/execveat/execveat03.c b/testcases/kernel/syscalls/execveat/execveat03.c index 057d8327..684f0d0c 100755 --- a/testcases/kernel/syscalls/execveat/execveat03.c +++ b/testcases/kernel/syscalls/execveat/execveat03.c @@ -68,6 +68,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 1, .needs_root = 1, .mount_device = 1, .needs_overlay = 1, diff --git a/testcases/kernel/syscalls/exit/exit01.c b/testcases/kernel/syscalls/exit/exit01.c index 1c15f43b..ca62dba4 100755 --- a/testcases/kernel/syscalls/exit/exit01.c +++ b/testcases/kernel/syscalls/exit/exit01.c @@ -52,7 +52,7 @@ int main(int ac, char **av) sig = 0; exno = 1; - pid = FORK_OR_VFORK(); + pid = tst_fork(); switch (pid) { case 0: diff --git a/testcases/kernel/syscalls/exit_group/Makefile b/testcases/kernel/syscalls/exit_group/Makefile index 1273a4e9..adbac3c5 100755 --- a/testcases/kernel/syscalls/exit_group/Makefile +++ b/testcases/kernel/syscalls/exit_group/Makefile @@ -3,6 +3,8 @@ top_srcdir ?= ../../../.. +exit_group01: CFLAGS+=-pthread + include $(top_srcdir)/include/mk/testcases.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/exit_group/exit_group01.c b/testcases/kernel/syscalls/exit_group/exit_group01.c index 5bf5b021..9005f467 100755 --- a/testcases/kernel/syscalls/exit_group/exit_group01.c +++ b/testcases/kernel/syscalls/exit_group/exit_group01.c @@ -1,68 +1,125 @@ -/****************************************************************************** - * Copyright (c) Crackerjack Project., 2007 * - * Ported to LTP by Manas Kumar Nayak * - * Copyright (C) 2015 Cyril Hrubis * - * * - * 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 * - * * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Crackerjack Project., 2007 + * Ported to LTP by Manas Kumar Nayak + * Copyright (c) 2015 Linux Test Project + * Copyright (C) 2015 Cyril Hrubis + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ +/*\ + * This test checks if exit_group() correctly ends a spawned child and all its + * running threads. + */ + +#include #include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" +#include +#include "tst_test.h" #include "lapi/syscalls.h" +#include "tst_safe_pthread.h" -char *TCID = "exit_group01"; -int testno; -int TST_TOTAL = 1; +static int cpu_count; -static void verify_exit_group(void) +static struct worker_data { + pid_t tid; + tst_atomic_t counter; +} *workers_data; + +static void *worker(void *arg) { - pid_t cpid, w; - int status; + struct worker_data *data; - cpid = fork(); - if (cpid == -1) - tst_brkm(TFAIL | TERRNO, NULL, "fork failed"); + data = (struct worker_data *)arg; + data->tid = tst_gettid(); - if (cpid == 0) { - TEST(tst_syscall(__NR_exit_group, 4)); - } else { - w = SAFE_WAIT(NULL, &status); + while (1) { + tst_atomic_inc(&data->counter); + sched_yield(); + } - if (WIFEXITED(status) && (WEXITSTATUS(status) == 4)) { - tst_resm(TPASS, "exit_group() succeeded"); - } else { - tst_resm(TFAIL | TERRNO, - "exit_group() failed (wait status = %d)", w); + return arg; +} + +static void spawn_threads(void) +{ + pthread_t threads[cpu_count]; + + for (int i = 0; i < cpu_count; i++) + SAFE_PTHREAD_CREATE(&threads[i], NULL, worker, (void *)(workers_data + i)); +} + +static void check_counters(void) +{ + struct worker_data data_copy[cpu_count]; + + memset(data_copy, 0, sizeof(struct worker_data) * cpu_count); + memcpy(data_copy, workers_data, sizeof(struct worker_data) * cpu_count); + + tst_res(TINFO, "Checking if threads are still running"); + usleep(100000); + + struct worker_data *old_data; + struct worker_data *new_data; + + for (int i = 0; i < cpu_count; i++) { + old_data = data_copy + i; + new_data = workers_data + i; + + if (old_data->counter != new_data->counter) { + tst_res(TFAIL, "Counter value has changed for thread[%d]", i); + return; } } + + tst_res(TINFO, "Threads counters value didn't change"); } -int main(int ac, char **av) +static void run(void) { - int lc; + pid_t pid; + int status; - tst_parse_opts(ac, av, NULL, NULL); + pid = SAFE_FORK(); + if (!pid) { + spawn_threads(); - for (lc = 0; TEST_LOOPING(lc); lc++) - verify_exit_group(); + TEST(tst_syscall(__NR_exit_group, 4)); + if (TST_RET == -1) + tst_brk(TBROK | TERRNO, "exit_group() error"); - tst_exit(); + return; + } + + SAFE_WAITPID(pid, &status, 0); + + TST_EXP_EXPR(WIFEXITED(status) && WEXITSTATUS(status) == 4, + "exit_group() succeeded"); + + check_counters(); } + +static void setup(void) +{ + cpu_count = MAX(2, tst_ncpus()); + + workers_data = SAFE_MMAP( + NULL, + sizeof(struct worker_data) * cpu_count, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, + -1, 0); +} + +static void cleanup(void) +{ + SAFE_MUNMAP(workers_data, sizeof(struct worker_data) * cpu_count); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .forks_child = 1, + .needs_checkpoints = 1, +}; diff --git a/testcases/kernel/syscalls/faccessat/faccessat01.c b/testcases/kernel/syscalls/faccessat/faccessat01.c index 557d7eb4..2c54fe2b 100755 --- a/testcases/kernel/syscalls/faccessat/faccessat01.c +++ b/testcases/kernel/syscalls/faccessat/faccessat01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check the basic functionality of the faccessat() system call. * * - faccessat() passes if dir_fd is file descriptor to the directory @@ -55,10 +53,7 @@ static void verify_faccessat(unsigned int i) static void setup(void) { - char *tmpdir_path = tst_get_tmpdir(); - - abs_path = tst_aprintf("%s/%s", tmpdir_path, FILEPATH); - free(tmpdir_path); + abs_path = tst_tmpdir_genpath(FILEPATH); SAFE_MKDIR(TESTDIR, 0700); dir_fd = SAFE_OPEN(TESTDIR, O_DIRECTORY); diff --git a/testcases/kernel/syscalls/faccessat/faccessat02.c b/testcases/kernel/syscalls/faccessat/faccessat02.c index 1add695c..3f79a259 100644 --- a/testcases/kernel/syscalls/faccessat/faccessat02.c +++ b/testcases/kernel/syscalls/faccessat/faccessat02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * - faccessat() fails with ENOTDIR if dir_fd is file descriptor to the file * and pathname is relative path of the file. * diff --git a/testcases/kernel/syscalls/faccessat2/faccessat201.c b/testcases/kernel/syscalls/faccessat2/faccessat201.c index 01209175..0fa807e3 100644 --- a/testcases/kernel/syscalls/faccessat2/faccessat201.c +++ b/testcases/kernel/syscalls/faccessat2/faccessat201.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check the basic functionality of faccessat2(). * * Minimum Linux version required is v5.8. @@ -56,10 +54,7 @@ static void verify_faccessat2(unsigned int i) static void setup(void) { - char *tmpdir_path = tst_get_tmpdir(); - - abs_path = tst_aprintf("%s/%s", tmpdir_path, RELPATH); - free(tmpdir_path); + abs_path = tst_tmpdir_genpath(RELPATH); SAFE_MKDIR(TESTDIR, 0777); dir_fd = SAFE_OPEN(TESTDIR, O_DIRECTORY); diff --git a/testcases/kernel/syscalls/faccessat2/faccessat202.c b/testcases/kernel/syscalls/faccessat2/faccessat202.c index a60db2bf..0878457e 100644 --- a/testcases/kernel/syscalls/faccessat2/faccessat202.c +++ b/testcases/kernel/syscalls/faccessat2/faccessat202.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test basic error handling of faccessat2 syscall: * * - faccessat2() fails with EFAULT if pathname is a bad pathname point. diff --git a/testcases/kernel/syscalls/fadvise/posix_fadvise01.c b/testcases/kernel/syscalls/fadvise/posix_fadvise01.c index 71e6454d..9752da7b 100755 --- a/testcases/kernel/syscalls/fadvise/posix_fadvise01.c +++ b/testcases/kernel/syscalls/fadvise/posix_fadvise01.c @@ -1,23 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Red Hat Inc., 2007 + * Author: Masatake YAMATO */ -/* - * NAME - * posix_fadvise01.c - * - * DESCRIPTION - * Check the value that posix_fadvise returns for wrong ADVISE value. - * - * USAGE - * posix_fadvise01 - * - * HISTORY - * 11/2007 Initial version by Masatake YAMATO - * - * RESTRICTIONS - * None +/*\ + * Verify that posix_fadvise() returns 0 for permissible ADVISE value. */ #include diff --git a/testcases/kernel/syscalls/fadvise/posix_fadvise02.c b/testcases/kernel/syscalls/fadvise/posix_fadvise02.c index 303f776e..1aba217a 100755 --- a/testcases/kernel/syscalls/fadvise/posix_fadvise02.c +++ b/testcases/kernel/syscalls/fadvise/posix_fadvise02.c @@ -1,23 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Red Hat Inc., 2007 + * Author: Masatake YAMATO */ -/* - * NAME - * posix_fadvise02.c - * - * DESCRIPTION - * Check the value that posix_fadvise returns for wrong file descriptor. - * - * USAGE - * posix_fadvise02 - * - * HISTORY - * 11/2007 Initial version by Masatake YAMATO - * - * RESTRICTIONS - * None +/*\ + * Verify that posix_fadvise() returns EBADF for wrong file descriptor. */ #include diff --git a/testcases/kernel/syscalls/fadvise/posix_fadvise03.c b/testcases/kernel/syscalls/fadvise/posix_fadvise03.c index 98d8d293..43d96b66 100755 --- a/testcases/kernel/syscalls/fadvise/posix_fadvise03.c +++ b/testcases/kernel/syscalls/fadvise/posix_fadvise03.c @@ -1,23 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Red Hat Inc., 2007 + * Author: Masatake YAMATO */ -/* - * NAME - * posix_fadvise03.c - * - * DESCRIPTION - * Check the value that posix_fadvise returns for wrong ADVISE value. - * - * USAGE - * posix_fadvise03 - * - * HISTORY - * 11/2007 Initial version by Masatake YAMATO - * - * RESTRICTIONS - * None +/*\ + * Verify that posix_fadvise() returns EINVAL for the ADVISE value not + * permissible on the architecture. */ #include diff --git a/testcases/kernel/syscalls/fadvise/posix_fadvise04.c b/testcases/kernel/syscalls/fadvise/posix_fadvise04.c index 8baf91bb..d080415b 100755 --- a/testcases/kernel/syscalls/fadvise/posix_fadvise04.c +++ b/testcases/kernel/syscalls/fadvise/posix_fadvise04.c @@ -1,23 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Red Hat Inc., 2007 + * Author: Masatake YAMATO */ -/* - * NAME - * posix_fadvise04.c - * - * DESCRIPTION - * Check the value that posix_fadvise returns for pipe descriptor. - * - * USAGE - * posix_fadvise04 - * - * HISTORY - * 11/2007 Initial version by Masatake YAMATO - * - * RESTRICTIONS - * None +/*\ + * Verify that posix_fadvise() returns ESPIPE for pipe descriptor. */ #include diff --git a/testcases/kernel/syscalls/fallocate/fallocate03.c b/testcases/kernel/syscalls/fallocate/fallocate03.c index da2b3bfb..dc15b876 100755 --- a/testcases/kernel/syscalls/fallocate/fallocate03.c +++ b/testcases/kernel/syscalls/fallocate/fallocate03.c @@ -1,277 +1,94 @@ -/****************************************************************************** - * fallocate03.c - * Mon Dec 24 2007 - * Copyright (c) International Business Machines Corp., 2007 - * Emali : sharyathi@in.ibm.com - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0-or-later -/*************************************************************************** - * 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 Library 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. -***************************************************************************/ +/* + * Copyright (c) International Business Machines Corp., 2007 + * Author: Sharyathi Nagesh + * Copyright (c) Linux Test Project, 2008-2017 + * Copyright (C) 2024 SUSE LLC Andrea Manzini + */ -/***************************************************************************** - * - * OS Test - International Business Machines Corp. 2007. - * - * TEST IDENTIFIER : fallocate03 - * - * EXECUTED BY : anyone - * - * TEST TITLE : fallocate - * - * TEST CASE TOTAL : 8 - * - * CPU ARCHITECTURES : PPC,X86, X86_64 - * - * AUTHOR : Sharyathi Nagesh - * - * CO-PILOT : - * - * DATE STARTED : 24/12/2007 - * - * TEST CASES - * (Working of fallocate on a sparse file) - * - * - * INPUT SPECIFICATIONS - * No input needs to be specified - * fallocate() in-puts are specified through test_data - * - * OUTPUT SPECIFICATIONS - * Output describing whether test cases passed or failed. - * - * ENVIRONMENTAL NEEDS - * Test Needs to be executed on file system supporting ext4 - * LTP {TMP} Needs to be set to such a folder - * - * SPECIAL PROCEDURAL REQUIREMENTS - * None - * - * DETAILED DESCRIPTION - * This is a test case for fallocate() system call. - * This test suite tests working of fallocate on sparse file - * fallocate is tested for different offsets - * - * Total 8 Test Cases :- - * Different offsets with in a sparse file is tested - * - * Setup: - * Setup file on which fallocate is to be called - * Set up a file with hole, created through lseek - * - * Test: - * Loop if the proper options are given - * Execute system call - * Check return code, if system call failed - * TEST fails, PASS the test otherwise - * - * Cleanup: - * Cleanup the temporary folder - * -*************************************************************************/ +/*\ + * Test fallocate() on sparse file for different offsets, with a total of 8 test cases + */ #define _GNU_SOURCE -#include -#include -#include -#include -#include -#include //Can be done with out #include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" -#include "lapi/fallocate.h" +#include "tst_test.h" #define BLOCKS_WRITTEN 12 #define HOLE_SIZE_IN_BLOCKS 12 #define DEFAULT_MODE 0 -#define TRUE 0 -void get_blocksize(int); -void populate_file(); -void file_seek(off_t); - -char *TCID = "fallocate03"; -char fname[255]; -int fd; -struct test_data_t { +static int fd; +static struct test_case { int mode; loff_t offset; - loff_t len; - int error; -} test_data[] = { - { - DEFAULT_MODE, 2, 1, TRUE}, { - DEFAULT_MODE, BLOCKS_WRITTEN, 1, TRUE}, { - DEFAULT_MODE, BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS / 2 - 1, 1, TRUE}, - { - DEFAULT_MODE, BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS + 1, 1, TRUE}, { - FALLOC_FL_KEEP_SIZE, 2, 1, TRUE}, { - FALLOC_FL_KEEP_SIZE, BLOCKS_WRITTEN, 1, TRUE}, { - FALLOC_FL_KEEP_SIZE, - BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS / 2 + 1, 1, TRUE}, { - FALLOC_FL_KEEP_SIZE, BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS + 2, - 1, TRUE} +} test_cases[] = { + {DEFAULT_MODE, 2}, + {DEFAULT_MODE, BLOCKS_WRITTEN}, + {DEFAULT_MODE, BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS / 2 - 1}, + {DEFAULT_MODE, BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS + 1}, + {FALLOC_FL_KEEP_SIZE, 2}, + {FALLOC_FL_KEEP_SIZE, BLOCKS_WRITTEN}, + {FALLOC_FL_KEEP_SIZE, BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS / 2 + 1}, + {FALLOC_FL_KEEP_SIZE, BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS + 2} }; -int TST_TOTAL = sizeof(test_data) / sizeof(test_data[0]); -int block_size; -int buf_size; +static int block_size; -/****************************************************************************** - * Performs all one time clean up for this test on successful - * completion, premature exit or failure. Closes all temporary - * files, removes all temporary directories exits the test with - * appropriate return code by calling tst_exit() function. -******************************************************************************/ -void cleanup(void) +static void cleanup(void) { - /* Close all open file descriptors. */ - if (close(fd) == -1) - tst_resm(TWARN | TERRNO, "close(%s) failed", fname); - - tst_rmdir(); - + if (fd) + SAFE_CLOSE(fd); } -/***************************************************************************** - * Performs all one time setup for this test. This function is - * used to create temporary dirs and temporary files - * that may be used in the course of this test - ******************************************************************************/ - -void setup(void) -{ - /* Create temporary directories */ - TEST_PAUSE; - - tst_tmpdir(); - - sprintf(fname, "tfile_sparse_%d", getpid()); - fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); - get_blocksize(fd); - populate_file(); - file_seek(BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS); /* create holes */ - populate_file(); - file_seek(0); /* Rewind */ -} - -/***************************************************************************** - * Gets the block size for the file system - ******************************************************************************/ -void get_blocksize(int fd) -{ - struct stat file_stat; - - if (fstat(fd, &file_stat) < 0) - tst_resm(TFAIL | TERRNO, - "fstat failed while getting block_size"); - - block_size = (int)file_stat.st_blksize; - buf_size = block_size; -} - -/***************************************************************************** - * Create a Hole in the file - ******************************************************************************/ -void file_seek(off_t offset) -{ - offset *= block_size; - lseek(fd, offset, SEEK_SET); -} - -/***************************************************************************** - * Writes data into the file - ******************************************************************************/ -void populate_file(void) +static void write_data_to_file(int buf_size) { char buf[buf_size + 1]; - int index; - int blocks; - int data; - for (blocks = 0; blocks < BLOCKS_WRITTEN; blocks++) { - for (index = 0; index < buf_size; index++) - buf[index] = 'A' + (index % 26); + + for (int blocks = 0; blocks < BLOCKS_WRITTEN; blocks++) { + for (int i = 0; i < buf_size; i++) + buf[i] = 'A' + (i % 26); + buf[buf_size] = '\0'; - if ((data = write(fd, buf, buf_size)) < 0) - tst_brkm(TBROK | TERRNO, cleanup, - "Unable to write to %s", fname); + SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, buf_size); } } -/***************************************************************************** - * Main function that calls the system call with the appropriate parameters - ******************************************************************************/ -/* ac: number of command line parameters */ -/* av: pointer to the array of the command line parameters */ -int main(int ac, char **av) +static void setup(void) { - int test_index = 0; - int lc; + char fname[NAME_MAX]; + struct stat file_stat; - /*************************************************************** - * parse standard options - ***************************************************************/ - tst_parse_opts(ac, av, NULL, NULL); + sprintf(fname, "tfile_sparse_%d", getpid()); + fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700); - /* perform global test setup, call setup() function */ - setup(); + SAFE_FSTAT(fd, &file_stat); + block_size = (int)file_stat.st_blksize; - for (lc = 0; TEST_LOOPING(lc); lc++) { - /* reset tst_count in case we are looping */ - tst_count = 0; - for (test_index = 0; test_index < TST_TOTAL; test_index++) { - TEST(fallocate - (fd, test_data[test_index].mode, - test_data[test_index].offset * block_size, - test_data[test_index].len * block_size)); - - /* check return code */ - if (TEST_RETURN != test_data[test_index].error) { - if (TEST_ERRNO == EOPNOTSUPP - || TEST_ERRNO == ENOSYS) { - tst_brkm(TCONF, cleanup, - "fallocate system call is not implemented"); - } - tst_resm(TFAIL | TTERRNO, - "fallocate(%s, %d, %" PRId64 ", %" - PRId64 ") failed", fname, - test_data[test_index].mode, - test_data[test_index].offset * - block_size, - test_data[test_index].len * - block_size); - } else { - tst_resm(TPASS, - "fallocate(%s, %d, %" PRId64 - ", %" PRId64 ") returned %ld", - fname, - test_data[test_index].mode, - test_data[test_index].offset * - block_size, - test_data[test_index].len * - block_size, TEST_RETURN); - } - } - } - - cleanup(); - tst_exit(); + write_data_to_file(block_size); + SAFE_LSEEK(fd, block_size * (BLOCKS_WRITTEN + HOLE_SIZE_IN_BLOCKS), SEEK_SET); + write_data_to_file(block_size); + SAFE_LSEEK(fd, 0, SEEK_SET); } + + +static void verify_fallocate(unsigned int nr) +{ + struct test_case *tc = &test_cases[nr]; + + TST_EXP_PASS( + fallocate(fd, tc->mode, tc->offset * block_size, block_size), + "fallocate(fd, %s, %ld, %d)", + tc->mode ? "FALLOC_FL_KEEP_SIZE" : "DEFAULT_MODE", + tc->offset * block_size, block_size); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test = verify_fallocate, + .needs_tmpdir = 1, + .tcnt = ARRAY_SIZE(test_cases) +}; diff --git a/testcases/kernel/syscalls/fallocate/fallocate04.c b/testcases/kernel/syscalls/fallocate/fallocate04.c index ff372a9b..3a8ea5fa 100755 --- a/testcases/kernel/syscalls/fallocate/fallocate04.c +++ b/testcases/kernel/syscalls/fallocate/fallocate04.c @@ -148,11 +148,6 @@ static void test03(void) { tst_res(TINFO, "zeroing file space with FALLOC_FL_ZERO_RANGE"); - if (tst_kvercmp(3, 15, 0) < 0) { - tst_brk(TCONF, - "FALLOC_FL_ZERO_RANGE needs Linux 3.15 or newer"); - } - size_t alloc_size0 = get_allocsize(); tst_res(TINFO, "read current allocated file size '%zu'", alloc_size0); @@ -287,6 +282,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 9, .options = (struct tst_option[]) { {"v", &verbose, "Turns on verbose mode"}, {} diff --git a/testcases/kernel/syscalls/fallocate/fallocate05.c b/testcases/kernel/syscalls/fallocate/fallocate05.c index af6bf9e8..f17cc993 100755 --- a/testcases/kernel/syscalls/fallocate/fallocate05.c +++ b/testcases/kernel/syscalls/fallocate/fallocate05.c @@ -55,7 +55,7 @@ static void setup(void) static void run(void) { int fd; - long extsize, tmp; + long extsize, holesize, tmp; fd = SAFE_OPEN(MNTPOINT "/test_file", O_WRONLY | O_CREAT | O_TRUNC, 0644); @@ -114,12 +114,14 @@ static void run(void) tst_res(TPASS, "fallocate() on full FS"); /* Btrfs deallocates only complete extents, not individual blocks */ - if (!strcmp(tst_device->fs_type, "btrfs")) - tmp = bufsize + extsize; + if (!strcmp(tst_device->fs_type, "btrfs") || + !strcmp(tst_device->fs_type, "bcachefs")) + holesize = bufsize + extsize; else - tmp = DEALLOCATE_BLOCKS * blocksize; + holesize = DEALLOCATE_BLOCKS * blocksize; - TEST(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, tmp)); + TEST(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, + holesize)); if (TST_RET == -1) { if (TST_ERR == ENOTSUP) @@ -135,6 +137,31 @@ static void run(void) else tst_res(TPASS, "write()"); + /* Check that the deallocated file range is marked as a hole */ + TEST(lseek(fd, 0, SEEK_HOLE)); + + if (TST_RET == 0) { + tst_res(TPASS, "Test file contains hole at offset 0"); + } else if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "lseek(SEEK_HOLE) failed"); + } else { + tst_res(TFAIL | TTERRNO, + "Unexpected lseek(SEEK_HOLE) return value %ld", + TST_RET); + } + + TEST(lseek(fd, 0, SEEK_DATA)); + + if (TST_RET == holesize) { + tst_res(TPASS, "Test file data start at offset %ld", TST_RET); + } else if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "lseek(SEEK_DATA) failed"); + } else { + tst_res(TFAIL | TTERRNO, + "Unexpected lseek(SEEK_DATA) return value %ld", + TST_RET); + } + SAFE_CLOSE(fd); tst_purge_dir(MNTPOINT); } @@ -145,6 +172,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 42, .needs_root = 1, .mount_device = 1, .mntpoint = MNTPOINT, diff --git a/testcases/kernel/syscalls/fallocate/fallocate06.c b/testcases/kernel/syscalls/fallocate/fallocate06.c index 124fb7ea..3730bddc 100755 --- a/testcases/kernel/syscalls/fallocate/fallocate06.c +++ b/testcases/kernel/syscalls/fallocate/fallocate06.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Tests misaligned fallocate() * * Test scenario: @@ -260,6 +258,8 @@ static struct tst_test test = { .test = run, .tcnt = ARRAY_SIZE(testcase_list), .needs_root = 1, + .dev_min_size = 1024, + .timeout = 150, .mount_device = 1, .mntpoint = MNTPOINT, .all_filesystems = 1, diff --git a/testcases/kernel/syscalls/fanotify/.gitignore b/testcases/kernel/syscalls/fanotify/.gitignore index a0a7d20d..16af3db8 100755 --- a/testcases/kernel/syscalls/fanotify/.gitignore +++ b/testcases/kernel/syscalls/fanotify/.gitignore @@ -21,4 +21,5 @@ /fanotify21 /fanotify22 /fanotify23 +/fanotify24 /fanotify_child diff --git a/testcases/kernel/syscalls/fanotify/fanotify.h b/testcases/kernel/syscalls/fanotify/fanotify.h index 75a081dc..7977b4aa 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify.h +++ b/testcases/kernel/syscalls/fanotify/fanotify.h @@ -68,19 +68,14 @@ static inline int safe_fanotify_mark(const char *file, const int lineno, #ifdef HAVE_NAME_TO_HANDLE_AT -#ifndef MAX_HANDLE_SZ -#define MAX_HANDLE_SZ 128 -#endif - -#ifndef AT_HANDLE_FID -#define AT_HANDLE_FID 0x200 -#endif - /* * Helper function used to obtain fsid and file_handle for a given path. * Used by test files correlated to FAN_REPORT_FID functionality. + * + * Returns 0 if normal NFS file handles are supported. + * Returns AT_HANDLE_FID, if only non-decodeable file handles are supported. */ -static inline void fanotify_get_fid(const char *path, __kernel_fsid_t *fsid, +static inline int fanotify_get_fid(const char *path, __kernel_fsid_t *fsid, struct file_handle *handle) { int mount_id; @@ -93,6 +88,11 @@ static inline void fanotify_get_fid(const char *path, __kernel_fsid_t *fsid, if (name_to_handle_at(AT_FDCWD, path, handle, &mount_id, 0) == -1) { if (errno == EOPNOTSUPP) { + /* Try to request non-decodeable fid instead */ + if (name_to_handle_at(AT_FDCWD, path, handle, &mount_id, + AT_HANDLE_FID) == 0) + return AT_HANDLE_FID; + tst_brk(TCONF, "filesystem %s does not support file handles", tst_device->fs_type); @@ -100,30 +100,30 @@ static inline void fanotify_get_fid(const char *path, __kernel_fsid_t *fsid, tst_brk(TBROK | TERRNO, "name_to_handle_at(AT_FDCWD, %s, ...) failed", path); } + return 0; } -#ifndef FILEID_INVALID -#define FILEID_INVALID 0xff -#endif - struct fanotify_fid_t { __kernel_fsid_t fsid; struct file_handle handle; char buf[MAX_HANDLE_SZ]; }; -static inline void fanotify_save_fid(const char *path, +static inline int fanotify_save_fid(const char *path, struct fanotify_fid_t *fid) { int *fh = (int *)(fid->handle.f_handle); + int ret; fh[0] = fh[1] = fh[2] = 0; fid->handle.handle_bytes = MAX_HANDLE_SZ; - fanotify_get_fid(path, &fid->fsid, &fid->handle); + ret = fanotify_get_fid(path, &fid->fsid, &fid->handle); tst_res(TINFO, "fid(%s) = %x.%x.%x.%x.%x...", path, fid->fsid.val[0], fid->fsid.val[1], fh[0], fh[1], fh[2]); + + return ret; } #endif /* HAVE_NAME_TO_HANDLE_AT */ @@ -133,13 +133,14 @@ static inline void fanotify_save_fid(const char *path, #define INIT_FANOTIFY_MARK_TYPE(t) \ { FAN_MARK_ ## t, "FAN_MARK_" #t } -static inline void require_fanotify_access_permissions_supported_by_kernel(void) +static inline void require_fanotify_access_permissions_supported_on_fs( + const char *fname) { int fd; fd = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY); - if (fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD, ".") < 0) { + if (fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD, fname) < 0) { if (errno == EINVAL) { tst_brk(TCONF | TERRNO, "CONFIG_FANOTIFY_ACCESS_PERMISSIONS not configured in kernel?"); @@ -152,118 +153,137 @@ static inline void require_fanotify_access_permissions_supported_by_kernel(void) SAFE_CLOSE(fd); } -static inline int fanotify_events_supported_by_kernel(uint64_t mask, - unsigned int init_flags, - unsigned int mark_flags) -{ - int fd; - int rval = 0; - - fd = SAFE_FANOTIFY_INIT(init_flags, O_RDONLY); - - if (fanotify_mark(fd, FAN_MARK_ADD | mark_flags, mask, AT_FDCWD, ".") < 0) { - if (errno == EINVAL) { - rval = -1; - } else { - tst_brk(TBROK | TERRNO, - "fanotify_mark (%d, FAN_MARK_ADD, ..., AT_FDCWD, \".\") failed", fd); - } - } - - SAFE_CLOSE(fd); - - return rval; -} - /* - * @return 0: fanotify supported both in kernel and on tested filesystem - * @return -1: @flags not supported in kernel - * @return -2: @flags not supported on tested filesystem (tested if @fname is not NULL) + * @return 0: fanotify flags supported both in kernel and on tested filesystem + * @return -1: @init_flags not supported in kernel + * @return -2: @mark_flags not supported on tested filesystem (tested if @fname is not NULL) + * @return -3: @mark_flags not supported on overlayfs (tested if @fname == OVL_MNT) */ -static inline int fanotify_init_flags_supported_on_fs(unsigned int flags, const char *fname) +static inline int fanotify_flags_supported_on_fs(unsigned int init_flags, + unsigned int mark_flags, + uint64_t event_flags, + const char *fname) { int fd; int rval = 0; + int err = 0; - fd = fanotify_init(flags, O_RDONLY); - + fd = fanotify_init(init_flags, O_RDONLY); if (fd < 0) { + err = errno; if (errno == ENOSYS) tst_brk(TCONF, "fanotify not configured in kernel"); - - if (errno == EINVAL) - return -1; - - tst_brk(TBROK | TERRNO, "fanotify_init() failed"); + if (errno != EINVAL) + tst_brk(TBROK | TERRNO, + "fanotify_init(%x, O_RDONLY) failed", + init_flags); + errno = err; + return -1; } - if (fname && fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS, AT_FDCWD, fname) < 0) { + if (fname && fanotify_mark(fd, FAN_MARK_ADD | mark_flags, event_flags, AT_FDCWD, fname) < 0) { + err = errno; if (errno == ENODEV || errno == EOPNOTSUPP || errno == EXDEV) { - rval = -2; - } else { + rval = strcmp(fname, OVL_MNT) ? -2 : -3; + } else if (errno != EINVAL) { tst_brk(TBROK | TERRNO, - "fanotify_mark (%d, FAN_MARK_ADD, ..., AT_FDCWD, %s) failed", - fd, fname); + "fanotify_mark (%d, FAN_MARK_ADD | %x, %llx, AT_FDCWD, %s) failed", + fd, mark_flags, (unsigned long long)event_flags, + fname); + } else { + rval = -1; } } SAFE_CLOSE(fd); + errno = err; return rval; } -static inline int fanotify_init_flags_supported_by_kernel(unsigned int flags) +static inline int fanotify_init_flags_supported_on_fs(unsigned int flags, const char *fname) { - return fanotify_init_flags_supported_on_fs(flags, NULL); + return fanotify_flags_supported_on_fs(flags, FAN_MARK_INODE, FAN_ACCESS, fname); +} + +static inline int fanotify_mark_supported_on_fs(uint64_t flag, const char *fname) +{ + return fanotify_flags_supported_on_fs(FAN_CLASS_NOTIF, flag, FAN_ACCESS, fname); +} + +#define TST_FANOTIFY_INIT_KNOWN_FLAGS \ + (FAN_REPORT_DFID_NAME_TARGET | FAN_REPORT_TID | FAN_REPORT_PIDFD | \ + FAN_REPORT_FD_ERROR | \ + FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT) + +/* + * Check support of given init flags one by one and return those which are + * supported. + */ +static inline unsigned int fanotify_get_supported_init_flags(unsigned int flags, + const char *fname) +{ + unsigned int i, flg, arg, ret = 0; + static const struct { unsigned int flag, deps; } deplist[] = { + {FAN_REPORT_NAME, FAN_REPORT_DIR_FID}, + {FAN_REPORT_TARGET_FID, FAN_REPORT_DFID_NAME_FID}, + {0, 0} + }; + + if (flags & ~TST_FANOTIFY_INIT_KNOWN_FLAGS) { + tst_brk(TBROK, "fanotify_init() feature check called with unknown flags %x, please update flag dependency table if needed", + flags & ~TST_FANOTIFY_INIT_KNOWN_FLAGS); + } + + for (flg = 1; flg; flg <<= 1) { + if (!(flags & flg)) + continue; + + arg = flg; + + for (i = 0; deplist[i].flag; i++) { + if (deplist[i].flag == flg) { + arg |= deplist[i].deps; + break; + } + } + + if (!fanotify_init_flags_supported_on_fs(arg, fname)) + ret |= flg; + } + + return ret; } typedef void (*tst_res_func_t)(const char *file, const int lineno, int ttype, const char *fmt, ...); -static inline void fanotify_init_flags_err_msg(const char *flags_str, +static inline void fanotify_flags_err_msg(const char *flags_str, const char *file, const int lineno, tst_res_func_t res_func, int fail) { if (fail == -1) res_func(file, lineno, TCONF, "%s not supported in kernel?", flags_str); - if (fail == -2) + if (fail == -2 || fail == -3) res_func(file, lineno, TCONF, - "%s not supported on %s filesystem", - flags_str, tst_device->fs_type); + "%s not supported on %s%s filesystem", + flags_str, fail == -3 ? "overlayfs over " : "", + tst_device->fs_type); } #define FANOTIFY_INIT_FLAGS_ERR_MSG(flags, fail) \ - fanotify_init_flags_err_msg(#flags, __FILE__, __LINE__, tst_res_, (fail)) + fanotify_flags_err_msg(#flags, __FILE__, __LINE__, tst_res_, (fail)) + +#define FANOTIFY_MARK_FLAGS_ERR_MSG(mark, fail) \ + fanotify_flags_err_msg((mark)->name, __FILE__, __LINE__, tst_res_, (fail)) + +#define FANOTIFY_EVENTS_ERR_MSG(event, fail) \ + fanotify_flags_err_msg(#event, __FILE__, __LINE__, tst_res_, (fail)) #define REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(flags, fname) \ - fanotify_init_flags_err_msg(#flags, __FILE__, __LINE__, tst_brk_, \ + fanotify_flags_err_msg(#flags, __FILE__, __LINE__, tst_brk_, \ fanotify_init_flags_supported_on_fs(flags, fname)) -#define REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_BY_KERNEL(flags) \ - fanotify_init_flags_err_msg(#flags, __FILE__, __LINE__, tst_brk_, \ - fanotify_init_flags_supported_by_kernel(flags)) - -static inline int fanotify_mark_supported_by_kernel(uint64_t flag) -{ - int fd; - int rval = 0; - - fd = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY); - - if (fanotify_mark(fd, FAN_MARK_ADD | flag, FAN_ACCESS, AT_FDCWD, ".") < 0) { - if (errno == EINVAL) { - rval = -1; - } else { - tst_brk(TBROK | TERRNO, - "fanotify_mark (%d, FAN_MARK_ADD, ..., FAN_ACCESS, AT_FDCWD, \".\") failed", fd); - } - } - - SAFE_CLOSE(fd); - - return rval; -} - static inline int fanotify_handle_supported_by_kernel(int flag) { /* @@ -277,21 +297,21 @@ static inline int fanotify_handle_supported_by_kernel(int flag) return 0; } -#define REQUIRE_MARK_TYPE_SUPPORTED_BY_KERNEL(mark_type) \ - fanotify_init_flags_err_msg(#mark_type, __FILE__, __LINE__, tst_brk_, \ - fanotify_mark_supported_by_kernel(mark_type)) +#define REQUIRE_MARK_TYPE_SUPPORTED_ON_FS(mark_type, fname) \ + fanotify_flags_err_msg(#mark_type, __FILE__, __LINE__, tst_brk_, \ + fanotify_mark_supported_on_fs(mark_type, fname)) #define REQUIRE_HANDLE_TYPE_SUPPORTED_BY_KERNEL(handle_type) \ - fanotify_init_flags_err_msg(#handle_type, __FILE__, __LINE__, tst_brk_, \ - fanotify_handle_supported_by_kernel(handle_type)) + fanotify_flags_err_msg(#handle_type, __FILE__, __LINE__, tst_brk_, \ + fanotify_handle_supported_by_kernel(handle_type)) #define REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(init_flags, mark_type, mask, fname) do { \ if (mark_type) \ - REQUIRE_MARK_TYPE_SUPPORTED_BY_KERNEL(mark_type); \ + REQUIRE_MARK_TYPE_SUPPORTED_ON_FS(mark_type, fname); \ if (init_flags) \ REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(init_flags, fname); \ - fanotify_init_flags_err_msg(#mask, __FILE__, __LINE__, tst_brk_, \ - fanotify_events_supported_by_kernel(mask, init_flags, mark_type)); \ + fanotify_flags_err_msg(#mask, __FILE__, __LINE__, tst_brk_, \ + fanotify_flags_supported_on_fs(init_flags, mark_type, mask, fname)); \ } while (0) static inline struct fanotify_event_info_header *get_event_info( diff --git a/testcases/kernel/syscalls/fanotify/fanotify01.c b/testcases/kernel/syscalls/fanotify/fanotify01.c index 3538335c..df50d84a 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify01.c +++ b/testcases/kernel/syscalls/fanotify/fanotify01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 SUSE. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check that fanotify work for a file. */ @@ -76,6 +75,9 @@ static char fname[BUF_SIZE]; static char buf[BUF_SIZE]; static int fd_notify; static int fan_report_fid_unsupported; +static int tmpfs_report_fid_unsupported; +static int mount_mark_fid_unsupported; +static int inode_mark_fid_xdev; static int filesystem_mark_unsupported; static unsigned long long event_set[EVENT_MAX]; @@ -88,16 +90,22 @@ static void test_fanotify(unsigned int n) struct fanotify_mark_type *mark = &tc->mark; int fd, ret, len, i = 0, test_num = 0; int tst_count = 0; + int report_fid = (tc->init_flags & FAN_REPORT_FID); tst_res(TINFO, "Test #%d: %s", n, tc->tname); - if (fan_report_fid_unsupported && (tc->init_flags & FAN_REPORT_FID)) { + if (fan_report_fid_unsupported && report_fid) { FANOTIFY_INIT_FLAGS_ERR_MSG(FAN_REPORT_FID, fan_report_fid_unsupported); return; } if (filesystem_mark_unsupported && mark->flag == FAN_MARK_FILESYSTEM) { - tst_res(TCONF, "FAN_MARK_FILESYSTEM not supported in kernel?"); + FANOTIFY_MARK_FLAGS_ERR_MSG(mark, filesystem_mark_unsupported); + return; + } + + if (mount_mark_fid_unsupported && report_fid && mark->flag != FAN_MARK_INODE) { + FANOTIFY_MARK_FLAGS_ERR_MSG(mark, mount_mark_fid_unsupported); return; } @@ -321,6 +329,19 @@ pass: } + + /* + * Try to setup a bogus mark on test tmp dir, to check if marks on + * different filesystems are supported. + * When tested fs has zero fsid (e.g. fuse) and events are reported + * with fsid+fid, watching different filesystems is not supported. + */ + if (!tmpfs_report_fid_unsupported) { + ret = report_fid ? inode_mark_fid_xdev : 0; + TST_EXP_FD_OR_FAIL(fanotify_mark(fd_notify, FAN_MARK_ADD, FAN_CLOSE_WRITE, + AT_FDCWD, "."), ret); + } + /* Remove mark to clear FAN_MARK_IGNORED_SURV_MODIFY */ SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_REMOVE | mark->flag, FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN, @@ -341,7 +362,27 @@ static void setup(void) SAFE_FILE_PRINTF(fname, "1"); fan_report_fid_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_FID, fname); - filesystem_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_FILESYSTEM); + filesystem_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_FILESYSTEM, fname); + mount_mark_fid_unsupported = fanotify_flags_supported_on_fs(FAN_REPORT_FID, + FAN_MARK_MOUNT, + FAN_OPEN, fname); + /* + * When mount mark is not supported due to zero fsid (e.g. fuse) or if TMPDIR has + * non-uniform fsid (e.g. btrfs subvol), multi fs inode marks are not supported. + */ + if (mount_mark_fid_unsupported && errno == ENODEV) { + tst_res(TINFO, "filesystem %s does not support reporting events with fid from multi fs", + tst_device->fs_type); + inode_mark_fid_xdev = EXDEV; + } + + tmpfs_report_fid_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_FID, "."); + if (!tmpfs_report_fid_unsupported && + fanotify_flags_supported_on_fs(FAN_REPORT_FID, FAN_MARK_MOUNT, FAN_OPEN, ".") && + (errno == ENODEV || errno == EXDEV)) { + inode_mark_fid_xdev = EXDEV; + tst_res(TINFO | TERRNO, "TMPDIR does not support reporting events with fid from multi fs"); + } } static void cleanup(void) @@ -351,6 +392,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 10, .test = test_fanotify, .tcnt = ARRAY_SIZE(tcases), .setup = setup, @@ -358,6 +400,7 @@ static struct tst_test test = { .needs_root = 1, .mount_device = 1, .mntpoint = MOUNT_PATH, + .all_filesystems = 1, }; #else diff --git a/testcases/kernel/syscalls/fanotify/fanotify02.c b/testcases/kernel/syscalls/fanotify/fanotify02.c index 3321e552..34c77967 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify02.c +++ b/testcases/kernel/syscalls/fanotify/fanotify02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 SUSE. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check that fanotify work for children of a directory. */ diff --git a/testcases/kernel/syscalls/fanotify/fanotify03.c b/testcases/kernel/syscalls/fanotify/fanotify03.c index 0bd61587..876f6c3d 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify03.c +++ b/testcases/kernel/syscalls/fanotify/fanotify03.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 SUSE. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check that fanotify permission events work. */ @@ -121,12 +120,29 @@ static struct tcase { {FAN_OPEN_EXEC_PERM, FAN_DENY} } }, + { + "parent watching children, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events", + INIT_FANOTIFY_MARK_TYPE(PARENT), + FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM | FAN_EVENT_ON_CHILD, 2, + { + {FAN_ACCESS_PERM, FAN_DENY}, + {FAN_OPEN_EXEC_PERM, FAN_DENY} + } + }, + { + "parent not watching children, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events", + INIT_FANOTIFY_MARK_TYPE(PARENT), + FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM, 0, + { + } + }, }; -static void generate_events(void) +static void generate_events(struct tcase *tc) { int fd; char *const argv[] = {FILE_EXEC_PATH, NULL}; + int exp_ret, exp_errno = tc->event_count ? EPERM : 0; /* * Generate sequence of events @@ -136,13 +152,21 @@ static void generate_events(void) SAFE_WRITE(SAFE_WRITE_ANY, fd, fname, 1); SAFE_LSEEK(fd, 0, SEEK_SET); - if (read(fd, buf, BUF_SIZE) != -1) + exp_ret = exp_errno ? -1 : 1; + errno = 0; + if (read(fd, buf, BUF_SIZE) != exp_ret || errno != exp_errno) { + tst_res(TFAIL, "read() got errno %d (expected %d)", errno, exp_errno); exit(3); + } SAFE_CLOSE(fd); - if (execve(FILE_EXEC_PATH, argv, environ) != -1) + exp_ret = exp_errno ? -1 : 0; + errno = 0; + if (execve(FILE_EXEC_PATH, argv, environ) != exp_ret || errno != exp_errno) { + tst_res(TFAIL, "execve() got errno %d (expected %d)", errno, exp_errno); exit(5); + } } static void child_handler(int tmp) @@ -156,7 +180,7 @@ static void child_handler(int tmp) fd_notify = -1; } -static void run_child(void) +static void run_child(struct tcase *tc) { struct sigaction child_action; @@ -174,7 +198,7 @@ static void run_child(void) if (child_pid == 0) { /* Child will generate events now */ SAFE_CLOSE(fd_notify); - generate_events(); + generate_events(tc); exit(0); } } @@ -220,6 +244,12 @@ static int setup_mark(unsigned int n) fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY); + if (mark->flag == FAN_MARK_PARENT) { + SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD | mark->flag, + tc->mask, AT_FDCWD, MOUNT_PATH); + return 0; + } + for (; i < ARRAY_SIZE(files); i++) { SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD | mark->flag, tc->mask, AT_FDCWD, files[i]); @@ -237,7 +267,7 @@ static void test_fanotify(unsigned int n) if (setup_mark(n) != 0) return; - run_child(); + run_child(tc); /* * Process events @@ -320,14 +350,14 @@ static void test_fanotify(unsigned int n) static void setup(void) { - require_fanotify_access_permissions_supported_by_kernel(); - - filesystem_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_FILESYSTEM); - exec_events_unsupported = fanotify_events_supported_by_kernel(FAN_OPEN_EXEC_PERM, - FAN_CLASS_CONTENT, 0); sprintf(fname, MOUNT_PATH"/fname_%d", getpid()); SAFE_FILE_PRINTF(fname, "1"); + require_fanotify_access_permissions_supported_on_fs(fname); + filesystem_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_FILESYSTEM, fname); + exec_events_unsupported = fanotify_flags_supported_on_fs(FAN_CLASS_CONTENT, + 0, FAN_OPEN_EXEC_PERM, fname); + SAFE_CP(TEST_APP, FILE_EXEC_PATH); } @@ -343,6 +373,7 @@ static const char *const resource_files[] = { }; static struct tst_test test = { + .timeout = 1, .test = test_fanotify, .tcnt = ARRAY_SIZE(tcases), .setup = setup, diff --git a/testcases/kernel/syscalls/fanotify/fanotify04.c b/testcases/kernel/syscalls/fanotify/fanotify04.c index 8541a7b9..9625e6f9 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify04.c +++ b/testcases/kernel/syscalls/fanotify/fanotify04.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 SUSE. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check various fanotify special flags. */ diff --git a/testcases/kernel/syscalls/fanotify/fanotify05.c b/testcases/kernel/syscalls/fanotify/fanotify05.c index 04670cb1..5d97e036 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify05.c +++ b/testcases/kernel/syscalls/fanotify/fanotify05.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014 SUSE Linux. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check that fanotify overflow event is properly generated. * * [Algorithm] @@ -51,6 +50,10 @@ static struct tcase { "Limited queue", FAN_CLASS_NOTIF, }, + { + "Limited queue (FAN_REPORT_FD_ERROR)", + FAN_CLASS_NOTIF | FAN_REPORT_FD_ERROR, + }, { "Unlimited queue", FAN_CLASS_NOTIF | FAN_UNLIMITED_QUEUE, @@ -63,6 +66,8 @@ static char symlnk[BUF_SIZE]; static char fdpath[BUF_SIZE]; static int fd, fd_notify; +static int fd_error_unsupported; + static struct fanotify_event_metadata event; static void event_res(struct fanotify_event_metadata *event, int i) @@ -110,9 +115,15 @@ static void test_fanotify(unsigned int n) int len, nevents = 0, got_overflow = 0; int num_files = max_events + 1; int expect_overflow = !(tc->init_flags & FAN_UNLIMITED_QUEUE); + int nofd_err = tc->init_flags & FAN_REPORT_FD_ERROR ? -EBADF : FAN_NOFD; tst_res(TINFO, "Test #%d: %s", n, tc->tname); + if (fd_error_unsupported && (tc->init_flags & FAN_REPORT_FD_ERROR)) { + FANOTIFY_INIT_FLAGS_ERR_MSG(FAN_REPORT_FD_ERROR, fd_error_unsupported); + return; + } + fd_notify = SAFE_FANOTIFY_INIT(tc->init_flags | FAN_NONBLOCK, O_RDONLY); SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_MOUNT | FAN_MARK_ADD, FAN_OPEN, @@ -139,10 +150,10 @@ static void test_fanotify(unsigned int n) "read of notification event failed"); } if (!got_overflow) - tst_res(expect_overflow ? TFAIL : TPASS, "Overflow event not generated!\n"); + tst_res(expect_overflow ? TFAIL : TPASS, "Overflow event not generated!"); break; } - if (event.fd != FAN_NOFD) { + if (event.fd >= 0) { /* * Verify that events generated on unique files * are received by the same order they were generated. @@ -166,7 +177,7 @@ static void test_fanotify(unsigned int n) break; } if (event.mask == FAN_Q_OVERFLOW) { - if (got_overflow || event.fd != FAN_NOFD) { + if (got_overflow || event.fd != nofd_err) { tst_res(TFAIL, "%s overflow event: mask=%llx pid=%u fd=%d", got_overflow ? "unexpected" : "invalid", @@ -193,6 +204,8 @@ static void setup(void) fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY); SAFE_CLOSE(fd); + fd_error_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_FD_ERROR, "."); + /* In older kernels this limit is fixed in kernel */ if (access(SYSFS_MAX_EVENTS, F_OK) && errno == ENOENT) max_events = DEFAULT_MAX_EVENTS; @@ -208,6 +221,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 13, .test = test_fanotify, .tcnt = ARRAY_SIZE(tcases), .setup = setup, diff --git a/testcases/kernel/syscalls/fanotify/fanotify06.c b/testcases/kernel/syscalls/fanotify/fanotify06.c index 618c85a4..2e93840f 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify06.c +++ b/testcases/kernel/syscalls/fanotify/fanotify06.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014 SUSE. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check that fanotify properly merges ignore mask of an inode and mountpoint. */ @@ -236,6 +235,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .test = test_fanotify, .tcnt = ARRAY_SIZE(tcases), .setup = setup, diff --git a/testcases/kernel/syscalls/fanotify/fanotify07.c b/testcases/kernel/syscalls/fanotify/fanotify07.c index 396c8490..7734b185 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify07.c +++ b/testcases/kernel/syscalls/fanotify/fanotify07.c @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check that fanotify permission events are handled properly on instance destruction. */ @@ -189,10 +188,9 @@ static void test_fanotify(void) static void setup(void) { - require_fanotify_access_permissions_supported_by_kernel(); - sprintf(fname, "fname_%d", getpid()); SAFE_FILE_PRINTF(fname, "%s", fname); + require_fanotify_access_permissions_supported_on_fs(fname); } static void cleanup(void) diff --git a/testcases/kernel/syscalls/fanotify/fanotify08.c b/testcases/kernel/syscalls/fanotify/fanotify08.c index de57f04d..abbf0b8a 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify08.c +++ b/testcases/kernel/syscalls/fanotify/fanotify08.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017 RedHat. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Sanity check fanotify_init flag FAN_CLOEXEC by fcntl. */ diff --git a/testcases/kernel/syscalls/fanotify/fanotify09.c b/testcases/kernel/syscalls/fanotify/fanotify09.c index 3f2db470..2ed3d1eb 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify09.c +++ b/testcases/kernel/syscalls/fanotify/fanotify09.c @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check that fanotify handles events on children correctly when both parent and * subdir or mountpoint marks exist. */ @@ -29,7 +28,6 @@ * 7372e79c9eb9 fanotify: fix logic of reporting name info with watched parent * * Test cases #6-#7 are regression tests for commit: - * (from v5.19, unlikely to be backported thus not in .tags): * * e730558adffb fanotify: consistent behavior for parent not watching children */ @@ -380,9 +378,9 @@ static void test_fanotify(unsigned int n) return; } - if (tc->ignore && tst_kvercmp(5, 19, 0) < 0) { + if (tc->ignore && tst_kvercmp(5, 10, 0) < 0) { tst_res(TCONF, "ignored mask on parent dir has undefined " - "behavior on kernel < 5.19"); + "behavior on kernel < 5.10"); return; } @@ -480,9 +478,11 @@ static void test_fanotify(unsigned int n) static void setup(void) { - fan_report_dfid_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_DFID_NAME, - MOUNT_PATH); - ignore_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_IGNORE_SURV); + fan_report_dfid_unsupported = fanotify_flags_supported_on_fs(FAN_REPORT_DFID_NAME, + FAN_MARK_MOUNT, + FAN_OPEN, MOUNT_PATH); + ignore_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_IGNORE_SURV, + MOUNT_PATH); SAFE_MKDIR(MOUNT_NAME, 0755); SAFE_MOUNT(MOUNT_PATH, MOUNT_NAME, "none", MS_BIND, NULL); @@ -506,6 +506,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .test = test_fanotify, .tcnt = ARRAY_SIZE(tcases), .setup = setup, @@ -518,6 +519,7 @@ static struct tst_test test = { {"linux-git", "b469e7e47c8a"}, {"linux-git", "55bf882c7f13"}, {"linux-git", "7372e79c9eb9"}, + {"linux-git", "e730558adffb"}, {} } }; diff --git a/testcases/kernel/syscalls/fanotify/fanotify10.c b/testcases/kernel/syscalls/fanotify/fanotify10.c index d0e9194e..2d33416f 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify10.c +++ b/testcases/kernel/syscalls/fanotify/fanotify10.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014 SUSE. All Rights Reserved. * Copyright (c) 2018 CTERA Networks. All Rights Reserved. @@ -8,7 +8,6 @@ */ /*\ - * [Description] * Check that fanotify properly merges ignore mask of a mount mark * with a mask of an inode mark on the same group. Unlike the * prototype test fanotify06, do not use FAN_MODIFY event for the @@ -103,7 +102,6 @@ static int ignore_mark_unsupported; #define DROP_CACHES_FILE "/proc/sys/vm/drop_caches" #define CACHE_PRESSURE_FILE "/proc/sys/vm/vfs_cache_pressure" -static int old_cache_pressure; static pid_t child_pid; static int bind_mount_created; static unsigned int num_classes = NUM_CLASSES; @@ -515,6 +513,12 @@ static void drop_caches(void) if (syncfs(fd_syncfs) < 0) tst_brk(TBROK | TERRNO, "Unexpected error when syncing filesystem"); + /* + * In order to ensure that the inode can be released in the two-tier + * directory structure, drop_cache is required three times. + */ + SAFE_FILE_PRINTF(DROP_CACHES_FILE, "3"); + SAFE_FILE_PRINTF(DROP_CACHES_FILE, "3"); SAFE_FILE_PRINTF(DROP_CACHES_FILE, "3"); } @@ -874,13 +878,17 @@ static void setup(void) { int i; - exec_events_unsupported = fanotify_events_supported_by_kernel(FAN_OPEN_EXEC, - FAN_CLASS_CONTENT, 0); - filesystem_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_FILESYSTEM); - evictable_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_EVICTABLE); - ignore_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_IGNORE_SURV); - fan_report_dfid_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_DFID_NAME, - MOUNT_PATH); + exec_events_unsupported = fanotify_flags_supported_on_fs(FAN_CLASS_CONTENT, + 0, FAN_OPEN_EXEC, MOUNT_PATH); + filesystem_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_FILESYSTEM, + MOUNT_PATH); + evictable_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_EVICTABLE, + MOUNT_PATH); + ignore_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_IGNORE_SURV, + MOUNT_PATH); + fan_report_dfid_unsupported = fanotify_flags_supported_on_fs(FAN_REPORT_DFID_NAME, + FAN_MARK_MOUNT, + FAN_OPEN, MOUNT_PATH); if (fan_report_dfid_unsupported) { FANOTIFY_INIT_FLAGS_ERR_MSG(FAN_REPORT_DFID_NAME, fan_report_dfid_unsupported); /* Limit tests to legacy priority classes */ @@ -916,7 +924,6 @@ static void setup(void) SAFE_MKDIR(MNT2_PATH, 0755); mount_cycle(); - SAFE_FILE_SCANF(CACHE_PRESSURE_FILE, "%d", &old_cache_pressure); /* Set high priority for evicting inodes */ SAFE_FILE_PRINTF(CACHE_PRESSURE_FILE, "500"); } @@ -930,8 +937,6 @@ static void cleanup(void) if (bind_mount_created) SAFE_UMOUNT(MNT2_PATH); - SAFE_FILE_PRINTF(CACHE_PRESSURE_FILE, "%d", old_cache_pressure); - for (i = 0; i < max_file_multi; i++) { char path[PATH_MAX]; @@ -949,6 +954,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 10, .test = test_fanotify, .tcnt = ARRAY_SIZE(tcases), .test_variants = 2, @@ -962,6 +968,10 @@ static struct tst_test test = { TEST_APP, NULL }, + .save_restore = (const struct tst_path_val[]) { + {CACHE_PRESSURE_FILE, NULL, TST_SR_TCONF}, + {} + }, .tags = (const struct tst_tag[]) { {"linux-git", "9bdda4e9cf2d"}, {"linux-git", "2f02fd3fa13e"}, diff --git a/testcases/kernel/syscalls/fanotify/fanotify11.c b/testcases/kernel/syscalls/fanotify/fanotify11.c index 03583d84..594d5f9d 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify11.c +++ b/testcases/kernel/syscalls/fanotify/fanotify11.c @@ -6,7 +6,6 @@ */ /*\ - * [Description] * After fanotify_init adds flags FAN_REPORT_TID, * check whether the program can accurately identify which thread id * in the multithreaded program triggered the event. @@ -94,7 +93,7 @@ static void test01(unsigned int i) static void setup(void) { - fan_report_tid_unsupported = fanotify_init_flags_supported_by_kernel(FAN_REPORT_TID); + fan_report_tid_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_TID, "."); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/fanotify/fanotify12.c b/testcases/kernel/syscalls/fanotify/fanotify12.c index 7f8e97b1..e5624835 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify12.c +++ b/testcases/kernel/syscalls/fanotify/fanotify12.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Validate that the newly introduced FAN_OPEN_EXEC mask functions as expected. * The idea is to generate a sequence of open related actions to ensure that * the correct event flags are being set depending on what event mask was @@ -222,11 +221,10 @@ cleanup: static void do_setup(void) { - exec_events_unsupported = fanotify_events_supported_by_kernel(FAN_OPEN_EXEC, - FAN_CLASS_NOTIF, 0); - sprintf(fname, "fname_%d", getpid()); SAFE_FILE_PRINTF(fname, "1"); + exec_events_unsupported = fanotify_flags_supported_on_fs(FAN_CLASS_NOTIF, + 0, FAN_OPEN_EXEC, fname); } static void do_cleanup(void) diff --git a/testcases/kernel/syscalls/fanotify/fanotify13.c b/testcases/kernel/syscalls/fanotify/fanotify13.c index a25a360f..32b2b823 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify13.c +++ b/testcases/kernel/syscalls/fanotify/fanotify13.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Validate that the values returned within an event when FAN_REPORT_FID is * specified matches those that are obtained via explicit invocation to system * calls statfs(2) and name_to_handle_at(2). @@ -15,6 +14,15 @@ /* * This is also regression test for: * c285a2f01d69 ("fanotify: update connector fsid cache on add mark") + * + * The test variants 1-2 are regression tests for: + * bc2473c90fca ("ovl: enable fsnotify events on underlying real files") + * + * The test variants 3-4 are tests for overlay fid events supprted since v6.6: + * 16aac5ad1fa9 ("ovl: support encoding non-decodable file handles") + * + * The last test case for FAN_DELETE_SELF is a regression test for: + * c45beebfde34a ("ovl: support encoding fid from inode with no alias") */ #define _GNU_SOURCE @@ -34,12 +42,12 @@ #include "fanotify.h" #define PATH_LEN 128 -#define BUF_SIZE 256 +#define BUF_SIZE 1024 #define DIR_ONE "dir_one" #define FILE_ONE "file_one" #define FILE_TWO "file_two" #define MOUNT_PATH "tstmnt" -#define EVENT_MAX ARRAY_SIZE(objects) +#define EVENT_MAX (ARRAY_SIZE(objects)+1) #define DIR_PATH_ONE MOUNT_PATH"/"DIR_ONE #define FILE_PATH_ONE MOUNT_PATH"/"FILE_ONE #define FILE_PATH_TWO MOUNT_PATH"/"FILE_TWO @@ -86,13 +94,20 @@ static struct test_case_t { { INIT_FANOTIFY_MARK_TYPE(FILESYSTEM), FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR - } + }, + /* Keep this test case last because it deletes the test files */ + { + INIT_FANOTIFY_MARK_TYPE(INODE), + FAN_DELETE_SELF | FAN_ONDIR + }, }; static int ovl_mounted; static int bind_mounted; +static int ovl_bind_mounted; static int nofid_fd; static int fanotify_fd; +static int at_handle_fid; static int filesystem_mark_unsupported; static char events_buf[BUF_SIZE]; static struct event_t event_set[EVENT_MAX]; @@ -109,12 +124,26 @@ static void create_objects(void) } } +static void delete_objects(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(objects); i++) { + if (objects[i].is_dir) + SAFE_RMDIR(objects[i].path); + else + SAFE_UNLINK(objects[i].path); + } +} + static void get_object_stats(void) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(objects); i++) - fanotify_save_fid(objects[i].path, &objects[i].fid); + for (i = 0; i < ARRAY_SIZE(objects); i++) { + at_handle_fid |= + fanotify_save_fid(objects[i].path, &objects[i].fid); + } } static int setup_marks(unsigned int fd, struct test_case_t *tc) @@ -126,10 +155,15 @@ static int setup_marks(unsigned int fd, struct test_case_t *tc) SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | mark->flag, tc->mask, AT_FDCWD, objects[i].path); - /* Setup the expected mask for each generated event */ + /* + * Setup the expected mask for each generated event. + * No events are expected on directory without FAN_ONDIR. + */ event_set[i].expected_mask = tc->mask; if (!objects[i].is_dir) event_set[i].expected_mask &= ~FAN_ONDIR; + else if (!(event_set[i].expected_mask & FAN_ONDIR)) + event_set[i].expected_mask = 0; } return 0; } @@ -146,20 +180,23 @@ static void do_test(unsigned int number) struct fanotify_mark_type *mark = &tc->mark; tst_res(TINFO, - "Test #%d.%d: FAN_REPORT_FID with mark flag: %s", - number, tst_variant, mark->name); + "Test #%d.%d: FAN_REPORT_FID of %s events with mark type %s", + number, tst_variant, + (tc->mask & FAN_DELETE_SELF) ? "delete" : "open/close", + mark->name); if (tst_variant && !ovl_mounted) { tst_res(TCONF, "overlayfs not supported on %s", tst_device->fs_type); return; } - if (filesystem_mark_unsupported && mark->flag & FAN_MARK_FILESYSTEM) { - tst_res(TCONF, "FAN_MARK_FILESYSTEM not supported in kernel?"); + if (filesystem_mark_unsupported && mark->flag != FAN_MARK_INODE) { + FANOTIFY_MARK_FLAGS_ERR_MSG(mark, filesystem_mark_unsupported); return; } - fanotify_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY); + fanotify_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_REPORT_FID | + FAN_NONBLOCK, O_RDONLY); /* * Place marks on a set of objects and setup the expected masks @@ -168,30 +205,47 @@ static void do_test(unsigned int number) if (setup_marks(fanotify_fd, tc) != 0) goto out; - /* Variant #1: watching upper fs - open files on overlayfs */ - if (tst_variant == 1) { + /* Watching base fs - open files on overlayfs */ + if (tst_variant && !ovl_bind_mounted) { if (mark->flag & FAN_MARK_MOUNT) { - tst_res(TCONF, "overlayfs upper fs cannot be watched with mount mark"); + tst_res(TCONF, "overlayfs base fs cannot be watched with mount mark"); + goto out; + } + if (tc->mask & FAN_DELETE_SELF) { + /* The eviction of base fs inodes is defered due to overlay held reference */ + tst_res(TCONF, "overlayfs base fs cannot be watched for delete self events"); goto out; } SAFE_MOUNT(OVL_MNT, MOUNT_PATH, "none", MS_BIND, NULL); } /* Generate sequence of FAN_OPEN events on objects */ - for (i = 0; i < ARRAY_SIZE(objects); i++) - fds[i] = SAFE_OPEN(objects[i].path, O_RDONLY); - - /* - * Generate sequence of FAN_CLOSE_NOWRITE events on objects. Each - * FAN_CLOSE_NOWRITE event is expected to be merged with its - * respective FAN_OPEN event that was performed on the same object. - */ - for (i = 0; i < ARRAY_SIZE(objects); i++) { - if (fds[i] > 0) - SAFE_CLOSE(fds[i]); + if (tc->mask & FAN_OPEN) { + for (i = 0; i < ARRAY_SIZE(objects); i++) + fds[i] = SAFE_OPEN(objects[i].path, O_RDONLY); } - if (tst_variant == 1) + /* + * Generate sequence of FAN_CLOSE_NOWRITE events on objects. + * Each FAN_CLOSE_NOWRITE event is expected to be merged with the + * respective FAN_OPEN event that was reported on the same object. + */ + if (tc->mask & FAN_CLOSE) { + for (i = 0; i < ARRAY_SIZE(objects); i++) { + if (fds[i] > 0) + SAFE_CLOSE(fds[i]); + } + } + + /* + * Generate sequence of FAN_DELETE_SELF events on objects. + * Each FAN_DELETE_SELF event is expected to be merged with the + * respective OPEN/CLOSE events that were reported on the same object. + */ + if (tc->mask & FAN_DELETE_SELF) + delete_objects(); + + if (tst_variant && !ovl_bind_mounted) SAFE_UMOUNT(MOUNT_PATH); /* Read events from event queue */ @@ -275,6 +329,16 @@ static void do_test(unsigned int number) FSID_VAL_MEMBER(event_fid->fsid, 1), *(unsigned long *) event_file_handle->f_handle); } + + /* + * Verify that we did not get an extra event, for example, that we did + * not get an event on directory without FAN_ONDIR. + */ + if (event_set[i].expected_mask) { + tst_res(TFAIL, + "Did not get an expected event (expected: %llx)", + event_set[i].expected_mask); + } out: SAFE_CLOSE(fanotify_fd); } @@ -286,19 +350,27 @@ static void do_setup(void) /* * Bind mount to either base fs or to overlayfs over base fs: * Variant #0: watch base fs - open files on base fs - * Variant #1: watch upper fs - open files on overlayfs + * Variant #1: watch lower fs - open lower files on overlayfs + * Variant #2: watch upper fs - open upper files on overlayfs + * Variant #3: watch overlayfs - open lower files on overlayfs + * Variant #4: watch overlayfs - open upper files on overlayfs * - * Variant #1 tests a bug whose fix bc2473c90fca ("ovl: enable fsnotify + * Variants 1,2 test a bug whose fix bc2473c90fca ("ovl: enable fsnotify * events on underlying real files") in kernel 6.5 is not likely to be * backported to older kernels. * To avoid waiting for events that won't arrive when testing old kernels, - * require that kernel supports encoding fid with new flag AT_HADNLE_FID, + * require that kernel supports encoding fid with new flag AT_HANDLE_FID, * also merged to 6.5 and not likely to be backported to older kernels. + * Variants 3,4 test overlayfs watch with FAN_REPORT_FID, which also + * requires kernel with support for AT_HANDLE_FID. */ if (tst_variant) { REQUIRE_HANDLE_TYPE_SUPPORTED_BY_KERNEL(AT_HANDLE_FID); ovl_mounted = TST_MOUNT_OVERLAY(); - mnt = OVL_UPPER; + if (!ovl_mounted) + return; + + mnt = tst_variant & 1 ? OVL_LOWER : OVL_UPPER; } else { mnt = OVL_BASE_MNTPOINT; @@ -308,13 +380,21 @@ static void do_setup(void) SAFE_MOUNT(mnt, MOUNT_PATH, "none", MS_BIND, NULL); bind_mounted = 1; - filesystem_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_FILESYSTEM); - nofid_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY); - /* Create file and directory objects for testing */ + /* Create file and directory objects for testing on base fs */ create_objects(); + if (tst_variant > 2) { + /* Setup watches on overlayfs */ + SAFE_MOUNT(OVL_MNT, MOUNT_PATH, "none", MS_BIND, NULL); + ovl_bind_mounted = 1; + } + + filesystem_mark_unsupported = + fanotify_flags_supported_on_fs(FAN_REPORT_FID, FAN_MARK_FILESYSTEM, FAN_OPEN, + ovl_bind_mounted ? OVL_MNT : MOUNT_PATH); + /* * Create a mark on first inode without FAN_REPORT_FID, to test * uninitialized connector->fsid cache. This mark remains for all test @@ -329,9 +409,12 @@ static void do_setup(void) static void do_cleanup(void) { - SAFE_CLOSE(nofid_fd); + if (nofid_fd > 0) + SAFE_CLOSE(nofid_fd); if (fanotify_fd > 0) SAFE_CLOSE(fanotify_fd); + if (ovl_bind_mounted) + SAFE_UMOUNT(MOUNT_PATH); if (bind_mounted) { SAFE_UMOUNT(MOUNT_PATH); SAFE_RMDIR(MOUNT_PATH); @@ -343,7 +426,7 @@ static void do_cleanup(void) static struct tst_test test = { .test = do_test, .tcnt = ARRAY_SIZE(test_cases), - .test_variants = 2, + .test_variants = 5, .setup = do_setup, .cleanup = do_cleanup, .needs_root = 1, @@ -353,6 +436,7 @@ static struct tst_test test = { .tags = (const struct tst_tag[]) { {"linux-git", "c285a2f01d69"}, {"linux-git", "bc2473c90fca"}, + {"linux-git", "c45beebfde34a"}, {} } }; diff --git a/testcases/kernel/syscalls/fanotify/fanotify14.c b/testcases/kernel/syscalls/fanotify/fanotify14.c index 4596511f..a00ca975 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify14.c +++ b/testcases/kernel/syscalls/fanotify/fanotify14.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved. * Copyright (c) Linux Test Project, 2020-2022 @@ -7,7 +7,6 @@ */ /*\ - * [Description] * This test file has been designed to ensure that the fanotify * system calls fanotify_init(2) and fanotify_mark(2) return the * correct error code to the calling process when an invalid flag or @@ -45,8 +44,10 @@ static int pipes[2] = {-1, -1}; static int fanotify_fd; -static int fan_report_target_fid_unsupported; static int ignore_mark_unsupported; +static int filesystem_mark_unsupported; +static int se_enforcing; +static unsigned int supported_init_flags; struct test_case_flags_t { unsigned long long flags; @@ -237,6 +238,26 @@ static struct test_case_t { .pfd = pipes, .expected_errno = EINVAL, }, + /* permission events in mask with priority < FAN_CLASS_CONTENT are not valid */ + { + .init = FLAGS_DESC(FAN_CLASS_NOTIF), + .mark = FLAGS_DESC(FAN_MARK_INODE), + .mask = FLAGS_DESC(LTP_ALL_PERM_EVENTS), + .expected_errno = EINVAL, + }, + /* pre-content events in mask with priority < FAN_CLASS_PRE_CONTENT are not valid */ + { + .init = FLAGS_DESC(FAN_CLASS_NOTIF), + .mark = FLAGS_DESC(FAN_MARK_INODE), + .mask = FLAGS_DESC(LTP_PRE_CONTENT_EVENTS), + .expected_errno = EINVAL, + }, + { + .init = FLAGS_DESC(FAN_CLASS_CONTENT), + .mark = FLAGS_DESC(FAN_MARK_INODE), + .mask = FLAGS_DESC(LTP_PRE_CONTENT_EVENTS), + .expected_errno = EINVAL, + }, }; static void do_test(unsigned int number) @@ -246,9 +267,8 @@ static void do_test(unsigned int number) tst_res(TINFO, "Test case %d: fanotify_init(%s, O_RDONLY)", number, tc->init.desc); - if (fan_report_target_fid_unsupported && tc->init.flags & FAN_REPORT_TARGET_FID) { - FANOTIFY_INIT_FLAGS_ERR_MSG(FAN_REPORT_TARGET_FID, - fan_report_target_fid_unsupported); + if (tc->init.flags & ~supported_init_flags) { + tst_res(TCONF, "Unsupported init flags"); return; } @@ -274,6 +294,7 @@ static void do_test(unsigned int number) /* Set mark on non-dir only when expecting error ENOTDIR */ const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT; + const int exp_errs[] = {tc->expected_errno, EACCES}; int dirfd = AT_FDCWD; if (tc->pfd) { @@ -283,9 +304,9 @@ static void do_test(unsigned int number) tst_res(TINFO, "Testing %s with %s", tc->mark.desc, tc->mask.desc); - TST_EXP_FD_OR_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags, - tc->mask.flags, dirfd, path), - tc->expected_errno); + + TST_EXP_FAIL_ARR(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags, + tc->mask.flags, dirfd, path), exp_errs, se_enforcing ? 2 : 1); /* * ENOTDIR are errors for events/flags not allowed on a non-dir inode. @@ -300,7 +321,7 @@ static void do_test(unsigned int number) "Adding an inode mark on directory did not fail with " "ENOTDIR error as on non-dir inode"); - if (!(tc->mark.flags & FAN_MARK_ONLYDIR)) { + if (!(tc->mark.flags & FAN_MARK_ONLYDIR) && !filesystem_mark_unsupported) { SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD | tc->mark.flags | FAN_MARK_FILESYSTEM, tc->mask.flags, AT_FDCWD, FILE1); @@ -317,17 +338,25 @@ out: static void do_setup(void) { + unsigned int all_init_flags = FAN_REPORT_DFID_NAME_TARGET | + FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT; + /* Require FAN_REPORT_FID support for all tests to simplify per test case requirements */ REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, MNTPOINT); + supported_init_flags = fanotify_get_supported_init_flags(all_init_flags, MNTPOINT); - fan_report_target_fid_unsupported = - fanotify_init_flags_supported_on_fs(FAN_REPORT_DFID_NAME_TARGET, MNTPOINT); - ignore_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_IGNORE_SURV); + ignore_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_IGNORE_SURV, + MNTPOINT); + filesystem_mark_unsupported = + fanotify_flags_supported_on_fs(FAN_REPORT_FID, FAN_MARK_FILESYSTEM, FAN_OPEN, + MNTPOINT); /* Create temporary test file to place marks on */ SAFE_FILE_PRINTF(FILE1, "0"); /* Create anonymous pipes to place marks on */ SAFE_PIPE2(pipes, O_CLOEXEC); + + se_enforcing = tst_selinux_enforcing(); } static void do_cleanup(void) diff --git a/testcases/kernel/syscalls/fanotify/fanotify15.c b/testcases/kernel/syscalls/fanotify/fanotify15.c index 6109d32c..2218dd66 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify15.c +++ b/testcases/kernel/syscalls/fanotify/fanotify15.c @@ -7,7 +7,6 @@ */ /*\ - * [Description] * Test file that has been purposely designed to verify FAN_REPORT_FID * functionality while using newly defined dirent events. */ @@ -52,6 +51,7 @@ struct event_t { }; static int fanotify_fd; +static int filesystem_mark_unsupported; static char events_buf[EVENT_BUF_LEN]; static struct event_t event_set[EVENT_MAX]; @@ -85,6 +85,11 @@ static void do_test(unsigned int number) tst_res(TINFO, "Test #%d: %s", number, tc->tname); + if (filesystem_mark_unsupported && mark->flag != FAN_MARK_INODE) { + FANOTIFY_MARK_FLAGS_ERR_MSG(mark, filesystem_mark_unsupported); + return; + } + SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD | mark->flag, tc->mask | FAN_CREATE | FAN_DELETE | FAN_MOVE | FAN_MODIFY | FAN_ONDIR, @@ -274,6 +279,10 @@ static void do_setup(void) { SAFE_MKDIR(TEST_DIR, 0755); REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_DIR); + filesystem_mark_unsupported = + fanotify_flags_supported_on_fs(FAN_REPORT_FID, FAN_MARK_FILESYSTEM, FAN_OPEN, + MOUNT_POINT); + fanotify_fd = SAFE_FANOTIFY_INIT(FAN_REPORT_FID, O_RDONLY); } diff --git a/testcases/kernel/syscalls/fanotify/fanotify16.c b/testcases/kernel/syscalls/fanotify/fanotify16.c index d45270a9..d4ad65bc 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify16.c +++ b/testcases/kernel/syscalls/fanotify/fanotify16.c @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check fanotify directory entry modification events, events on child and * on self with group init flags: * @@ -70,6 +69,7 @@ static char event_buf[EVENT_BUF_LEN]; #define TEMP_DIR MOUNT_PATH "/temp_dir" static int fan_report_target_fid_unsupported; +static int filesystem_mark_unsupported; static int rename_events_unsupported; static struct test_case_t { @@ -281,6 +281,16 @@ static void do_test(unsigned int number) return; } + if (filesystem_mark_unsupported) { + if (sub_mark && sub_mark->flag != FAN_MARK_INODE) + mark = sub_mark; + + if (mark->flag != FAN_MARK_INODE) { + FANOTIFY_MARK_FLAGS_ERR_MSG(mark, filesystem_mark_unsupported); + return; + } + } + fd_notify = SAFE_FANOTIFY_INIT(group->flag, 0); /* @@ -328,7 +338,15 @@ static void do_test(unsigned int number) tst_count++; /* Generate modify events "on child" */ - fd = SAFE_CREAT(fname1, 0755); + + /* + * Split SAFE_CREAT() into explicit SAFE_MKNOD() and SAFE_OPEN(), + * because with atomic open (e.g. fuse), SAFE_CREAT() generates + * FAN_OPEN before FAN_CREATE and it is inconsistent with the order + * of events expectated from other filesystems. + */ + SAFE_MKNOD(fname1, S_IFREG | 0644, 0); + fd = SAFE_OPEN(fname1, O_WRONLY); /* Save the file fid */ fanotify_save_fid(fname1, &file_fid); @@ -765,8 +783,12 @@ static void setup(void) REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_DIR_FID, MOUNT_PATH); fan_report_target_fid_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_DFID_NAME_TARGET, MOUNT_PATH); + filesystem_mark_unsupported = + fanotify_flags_supported_on_fs(FAN_REPORT_FID, FAN_MARK_FILESYSTEM, FAN_OPEN, + MOUNT_PATH); rename_events_unsupported = - fanotify_events_supported_by_kernel(FAN_RENAME, FAN_REPORT_DFID_NAME, 0); + fanotify_flags_supported_on_fs(FAN_REPORT_DFID_NAME, 0, + FAN_RENAME, MOUNT_PATH); SAFE_MKDIR(TEMP_DIR, 0755); sprintf(dname1, "%s/%s", MOUNT_PATH, DIR_NAME1); diff --git a/testcases/kernel/syscalls/fanotify/fanotify17.c b/testcases/kernel/syscalls/fanotify/fanotify17.c index 3ecb31b6..f6e282e7 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify17.c +++ b/testcases/kernel/syscalls/fanotify/fanotify17.c @@ -8,7 +8,6 @@ */ /*\ - * [Description] * Check that fanotify groups and marks limits are enforced correctly. * If user ns is supported, verify that global limit and per user ns * limits are both enforced. diff --git a/testcases/kernel/syscalls/fanotify/fanotify18.c b/testcases/kernel/syscalls/fanotify/fanotify18.c index 07b064b9..4fd99746 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify18.c +++ b/testcases/kernel/syscalls/fanotify/fanotify18.c @@ -6,7 +6,6 @@ */ /*\ - * [Description] * This set of tests is to ensure that the unprivileged listener feature of * fanotify is functioning as expected. The objective this test case file * is to validate whether any forbidden flags that are passed by an diff --git a/testcases/kernel/syscalls/fanotify/fanotify19.c b/testcases/kernel/syscalls/fanotify/fanotify19.c index 63fc4ee0..80790955 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify19.c +++ b/testcases/kernel/syscalls/fanotify/fanotify19.c @@ -6,7 +6,6 @@ */ /*\ - * [Description] * This set of tests is to ensure that the unprivileged listener feature of * fanotify is functioning as expected. The objective of this test file is * to generate a sequence of events and ensure that the returned events diff --git a/testcases/kernel/syscalls/fanotify/fanotify20.c b/testcases/kernel/syscalls/fanotify/fanotify20.c index 71310fb8..82d20492 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify20.c +++ b/testcases/kernel/syscalls/fanotify/fanotify20.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * This source file contains a test case which ensures that the fanotify API * returns an expected error code when provided an invalid initialization flag * alongside FAN_REPORT_PIDFD. Additionally, it checks that the operability with @@ -50,7 +48,8 @@ static void do_setup(void) * An explicit check for FAN_REPORT_PIDFD is performed early on in the * test initialization as it's a prerequisite for all test cases. */ - REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_BY_KERNEL(FAN_REPORT_PIDFD); + REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_PIDFD, + MOUNT_PATH); } static void do_test(unsigned int i) diff --git a/testcases/kernel/syscalls/fanotify/fanotify21.c b/testcases/kernel/syscalls/fanotify/fanotify21.c index 8a102808..c95895c9 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify21.c +++ b/testcases/kernel/syscalls/fanotify/fanotify21.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * A test which verifies whether the returned struct * fanotify_event_info_pidfd in FAN_REPORT_PIDFD mode contains the * expected set of information. @@ -21,6 +19,7 @@ #include #include #include +#include #include "tst_test.h" #include "tst_safe_stdio.h" #include "tst_safe_macros.h" @@ -45,16 +44,25 @@ static struct test_case_t { char *name; int fork; int want_pidfd_err; + int remount_ro; } test_cases[] = { { "return a valid pidfd for event created by self", 0, 0, + 0, }, { "return invalid pidfd for event created by terminated child", 1, - FAN_NOPIDFD, + 1, + 0, + }, + { + "fail to open rw fd for event created on read-only mount", + 0, + 0, + 1, }, }; @@ -62,6 +70,8 @@ static int fanotify_fd; static char event_buf[BUF_SZ]; static struct pidfd_fdinfo_t *self_pidfd_fdinfo; +static int fd_error_unsupported; + static struct pidfd_fdinfo_t *read_pidfd_fdinfo(int pidfd) { char *fdinfo_path; @@ -111,6 +121,15 @@ static void do_fork(void) static void do_setup(void) { int pidfd; + int init_flags = FAN_REPORT_PIDFD; + + if (tst_variant) { + fanotify_fd = -1; + fd_error_unsupported = fanotify_init_flags_supported_on_fs(FAN_REPORT_FD_ERROR, "."); + if (fd_error_unsupported) + return; + init_flags |= FAN_REPORT_FD_ERROR; + } SAFE_TOUCH(TEST_FILE, 0666, NULL); @@ -119,9 +138,10 @@ static void do_setup(void) * on in the test initialization as it's a prerequisite for * all test cases. */ - REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_BY_KERNEL(FAN_REPORT_PIDFD); + REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_PIDFD, + TEST_FILE); - fanotify_fd = SAFE_FANOTIFY_INIT(FAN_REPORT_PIDFD, O_RDONLY); + fanotify_fd = SAFE_FANOTIFY_INIT(init_flags, O_RDWR); SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, TEST_FILE); @@ -139,8 +159,27 @@ static void do_test(unsigned int num) { int i = 0, len; struct test_case_t *tc = &test_cases[num]; + int nopidfd_err = tc->want_pidfd_err ? + (tst_variant ? -ESRCH : FAN_NOPIDFD) : 0; + int fd_err = (tc->remount_ro && tst_variant) ? -EROFS : 0; - tst_res(TINFO, "Test #%d: %s", num, tc->name); + tst_res(TINFO, "Test #%d.%d: %s %s", num, tst_variant, tc->name, + tst_variant ? "(FAN_REPORT_FD_ERROR)" : ""); + + if (fd_error_unsupported && tst_variant) { + FANOTIFY_INIT_FLAGS_ERR_MSG(FAN_REPORT_FD_ERROR, fd_error_unsupported); + return; + } + + if (tc->remount_ro) { + /* SAFE_MOUNT fails to remount FUSE */ + if (mount(tst_device->dev, MOUNT_PATH, tst_device->fs_type, + MS_REMOUNT|MS_RDONLY, NULL) != 0) { + tst_brk(TFAIL, + "filesystem %s failed to remount readonly", + tst_device->fs_type); + } + } /* * Generate the event in either self or a child process. Event @@ -156,7 +195,16 @@ static void do_test(unsigned int num) * Read all of the queued events into the provided event * buffer. */ - len = SAFE_READ(0, fanotify_fd, event_buf, sizeof(event_buf)); + len = read(fanotify_fd, event_buf, sizeof(event_buf)); + if (len < 0) { + if (tc->remount_ro && !fd_err && errno == EROFS) { + tst_res(TPASS, "cannot read event with rw fd from a ro fs"); + return; + } + tst_brk(TBROK | TERRNO, "reading fanotify events failed"); + } else if (tc->remount_ro && !fd_err) { + tst_res(TFAIL, "got unexpected event with rw fd from a ro fs"); + } while (i < len) { struct fanotify_event_metadata *event; struct fanotify_event_info_pidfd *info; @@ -188,6 +236,28 @@ static void do_test(unsigned int num) goto next_event; } + /* + * Check if event->fd reported any errors during + * creation and whether they're expected. + */ + if (!fd_err && event->fd >= 0) { + tst_res(TPASS, + "event->fd %d is valid as expected", + event->fd); + } else if (fd_err && event->fd == fd_err) { + tst_res(TPASS, + "event->fd is error %d as expected", + event->fd); + } else if (fd_err) { + tst_res(TFAIL, + "event->fd is %d, but expected error %d", + event->fd, fd_err); + } else { + tst_res(TFAIL, + "event->fd creation failed with error %d", + event->fd); + } + /* * Check if pidfd information object reported any errors during * creation and whether they're expected. @@ -199,20 +269,18 @@ static void do_test(unsigned int num) (unsigned int)event->pid, info->pidfd); goto next_event; - } else if (tc->want_pidfd_err && - info->pidfd != tc->want_pidfd_err) { + } else if (tc->want_pidfd_err && info->pidfd != nopidfd_err) { tst_res(TFAIL, "pidfd set to an unexpected error: %d for pid: %u", info->pidfd, (unsigned int)event->pid); goto next_event; - } else if (tc->want_pidfd_err && - info->pidfd == tc->want_pidfd_err) { + } else if (tc->want_pidfd_err && info->pidfd == nopidfd_err) { tst_res(TPASS, "pid: %u terminated before pidfd was created, " "pidfd set to the value of: %d, as expected", (unsigned int)event->pid, - FAN_NOPIDFD); + nopidfd_err); goto next_event; } @@ -293,9 +361,11 @@ static struct tst_test test = { .setup = do_setup, .test = do_test, .tcnt = ARRAY_SIZE(test_cases), + .test_variants = 2, .cleanup = do_cleanup, .all_filesystems = 1, .needs_root = 1, + .mount_device = 1, .mntpoint = MOUNT_PATH, .forks_child = 1, }; diff --git a/testcases/kernel/syscalls/fanotify/fanotify22.c b/testcases/kernel/syscalls/fanotify/fanotify22.c index f4b7987d..357e74db 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify22.c +++ b/testcases/kernel/syscalls/fanotify/fanotify22.c @@ -7,7 +7,6 @@ */ /*\ - * [Description] * Check fanotify FAN_ERROR_FS events triggered by intentionally * corrupted filesystems: * @@ -62,7 +61,7 @@ static void trigger_fs_abort(void) static void do_debugfs_request(const char *dev, char *request) { - const char *const cmd[] = {"debugfs", "-w", dev, "-R", request, NULL}; + const char *const cmd[] = {"debugfs", "-w", "-R", request, dev, NULL}; SAFE_CMD(cmd, NULL, NULL); } @@ -296,7 +295,7 @@ static void setup(void) { REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(FAN_CLASS_NOTIF|FAN_REPORT_FID, FAN_MARK_FILESYSTEM, - FAN_FS_ERROR, "."); + FAN_FS_ERROR, MOUNT_PATH); pre_corrupt_fs(); fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF|FAN_REPORT_FID, @@ -319,9 +318,13 @@ static struct tst_test test = { .mount_device = 1, .mntpoint = MOUNT_PATH, .needs_root = 1, - .dev_fs_type = "ext4", + .filesystems = (struct tst_fs []){ + {.type = "ext4"}, + {} + }, .tags = (const struct tst_tag[]) { {"linux-git", "124e7c61deb2"}, + {"linux-git", "76486b104168"}, {} }, .needs_cmds = (const char *[]) { diff --git a/testcases/kernel/syscalls/fanotify/fanotify23.c b/testcases/kernel/syscalls/fanotify/fanotify23.c index fb812c51..36c7779d 100644 --- a/testcases/kernel/syscalls/fanotify/fanotify23.c +++ b/testcases/kernel/syscalls/fanotify/fanotify23.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2022 CTERA Networks. All Rights Reserved. * @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Check evictable fanotify inode marks. */ @@ -36,7 +35,6 @@ #define DROP_CACHES_FILE "/proc/sys/vm/drop_caches" #define CACHE_PRESSURE_FILE "/proc/sys/vm/vfs_cache_pressure" -static int old_cache_pressure; static int fd_notify; static unsigned long long event_set[EVENT_MAX]; @@ -230,12 +228,11 @@ static void setup(void) { SAFE_TOUCH(TEST_FILE, 0666, NULL); - REQUIRE_MARK_TYPE_SUPPORTED_BY_KERNEL(FAN_MARK_EVICTABLE); + REQUIRE_MARK_TYPE_SUPPORTED_ON_FS(FAN_MARK_EVICTABLE, MOUNT_PATH); REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(FAN_CLASS_NOTIF|FAN_REPORT_FID, FAN_MARK_FILESYSTEM, - FAN_ATTRIB, "."); + FAN_ATTRIB, MOUNT_PATH); - SAFE_FILE_SCANF(CACHE_PRESSURE_FILE, "%d", &old_cache_pressure); /* Set high priority for evicting inodes */ SAFE_FILE_PRINTF(CACHE_PRESSURE_FILE, "500"); } @@ -244,8 +241,6 @@ static void cleanup(void) { if (fd_notify > 0) SAFE_CLOSE(fd_notify); - - SAFE_FILE_PRINTF(CACHE_PRESSURE_FILE, "%d", old_cache_pressure); } static struct tst_test test = { @@ -256,7 +251,14 @@ static struct tst_test test = { .mount_device = 1, .mntpoint = MOUNT_PATH, /* Shrinkers on other fs do not work reliably enough to guarantee mark eviction on drop_caches */ - .dev_fs_type = "ext2", + .filesystems = (struct tst_fs []){ + {.type = "ext2"}, + {} + }, + .save_restore = (const struct tst_path_val[]) { + {CACHE_PRESSURE_FILE, NULL, TST_SR_TCONF}, + {} + }, }; #else diff --git a/testcases/kernel/syscalls/fanotify/fanotify24.c b/testcases/kernel/syscalls/fanotify/fanotify24.c new file mode 100644 index 00000000..27f0663c --- /dev/null +++ b/testcases/kernel/syscalls/fanotify/fanotify24.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2013 SUSE. All Rights Reserved. + * Copyright (c) 2025 CTERA Networks. All Rights Reserved. + * + * Started by Jan Kara + */ + +/*\ + * - Test fanotify pre-content events + * - Test respond to permission/pre-content events with cutsom error code + */ + +#define _GNU_SOURCE +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tst_test.h" + +#ifdef HAVE_SYS_FANOTIFY_H +# include "fanotify.h" + +#define EVENT_MAX 1024 +/* size of the event structure, not counting name */ +#define EVENT_SIZE (sizeof(struct fanotify_event_metadata)) +/* reasonable guess as to size of 1024 events */ +#define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE) +/* Size large enough to hold a reasonable amount of expected event objects */ +#define EVENT_SET_MAX 16 + +#define BUF_SIZE 256 +#define TST_TOTAL 3 +#define TEST_APP "fanotify_child" +#define MOUNT_PATH "fs_mnt" +#define FILE_EXEC_PATH MOUNT_PATH"/"TEST_APP + +static char fname[BUF_SIZE]; +static char buf[BUF_SIZE]; +static volatile int fd_notify; +static size_t page_sz; + +static pid_t child_pid; + +static char event_buf[EVENT_BUF_LEN]; + +struct event { + unsigned long long mask; + unsigned int response; +}; + +static struct tcase { + const char *tname; + struct fanotify_mark_type mark; + unsigned long long mask; + struct event event_set[EVENT_SET_MAX]; +} tcases[] = { + { + "inode mark, FAN_OPEN_PERM | FAN_PRE_ACCESS events", + INIT_FANOTIFY_MARK_TYPE(INODE), + FAN_OPEN_PERM | FAN_PRE_ACCESS, + { + {FAN_OPEN_PERM, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_DENY_ERRNO(EIO)}, + {FAN_OPEN_PERM, FAN_DENY_ERRNO(EBUSY)} + } + }, + { + "inode mark, FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM events", + INIT_FANOTIFY_MARK_TYPE(INODE), + FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM, + { + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_DENY}, + {FAN_PRE_ACCESS, FAN_DENY_ERRNO(EIO)}, + {FAN_OPEN_EXEC_PERM, FAN_DENY_ERRNO(EBUSY)} + } + }, + { + "mount mark, FAN_OPEN_PERM | FAN_PRE_ACCESS events", + INIT_FANOTIFY_MARK_TYPE(MOUNT), + FAN_OPEN_PERM | FAN_PRE_ACCESS, + { + {FAN_OPEN_PERM, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_DENY_ERRNO(EIO)}, + {FAN_OPEN_PERM, FAN_DENY_ERRNO(EBUSY)} + } + }, + { + "mount mark, FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM events", + INIT_FANOTIFY_MARK_TYPE(MOUNT), + FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM, + { + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_DENY}, + {FAN_PRE_ACCESS, FAN_DENY_ERRNO(EIO)}, + {FAN_OPEN_EXEC_PERM, FAN_DENY_ERRNO(EBUSY)} + } + }, + { + "filesystem mark, FAN_OPEN_PERM | FAN_PRE_ACCESS events", + INIT_FANOTIFY_MARK_TYPE(FILESYSTEM), + FAN_OPEN_PERM | FAN_PRE_ACCESS, + { + {FAN_OPEN_PERM, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_DENY_ERRNO(EIO)}, + {FAN_OPEN_PERM, FAN_DENY_ERRNO(EBUSY)} + } + }, + { + "filesystem mark, FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM events", + INIT_FANOTIFY_MARK_TYPE(FILESYSTEM), + FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM, + { + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_DENY}, + {FAN_PRE_ACCESS, FAN_DENY_ERRNO(EIO)}, + {FAN_OPEN_EXEC_PERM, FAN_DENY_ERRNO(EBUSY)} + } + }, + { + "parent watching children, FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM events", + INIT_FANOTIFY_MARK_TYPE(PARENT), + FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM | FAN_EVENT_ON_CHILD, + { + {FAN_PRE_ACCESS, FAN_ALLOW}, + {FAN_PRE_ACCESS, FAN_DENY}, + {FAN_PRE_ACCESS, FAN_DENY}, + {FAN_OPEN_EXEC_PERM, FAN_DENY} + } + }, + { + "parent not watching children, FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM events", + INIT_FANOTIFY_MARK_TYPE(PARENT), + FAN_PRE_ACCESS | FAN_OPEN_EXEC_PERM, + { + } + }, + { + "inode mark, FAN_PRE_ACCESS event allowed", + INIT_FANOTIFY_MARK_TYPE(INODE), + FAN_PRE_ACCESS, + { + /* This allows multiple FAN_PRE_ACCESS events */ + {FAN_PRE_ACCESS, FAN_ALLOW}, + } + }, +}; + +static int expected_errno(unsigned int response) +{ + switch (response) { + case 0: + case FAN_ALLOW: + return 0; + case FAN_DENY: + return EPERM; + default: + return FAN_RESPONSE_ERRNO(response); + } +} + +static void generate_events(struct tcase *tc) +{ + int fd; + char *addr; + char *const argv[] = {FILE_EXEC_PATH, NULL}; + struct event *event = tc->event_set; + int exp_ret, exp_errno = 0; + + if (event->mask == FAN_OPEN_PERM) + event++; + + /* + * Generate sequence of events + */ + fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700); + + exp_errno = expected_errno(event->response); + event++; + + exp_ret = exp_errno ? -1 : 1; + errno = 0; + /* + * FAN_PRE_ACCESS events are reported on map() and write(), but should + * not be reported when faulting in the user page at offset page_sz*100 + * that is used as an input buffer to the write() syscall. + */ + addr = SAFE_MMAP(NULL, page_sz, PROT_READ, MAP_PRIVATE, fd, page_sz*100); + if (!addr || errno != exp_errno) { + tst_res(TFAIL, "mmap() got errno %d (expected %d)", errno, exp_errno); + exit(3); + } else if (errno == exp_errno) { + tst_res(TINFO, "mmap() got errno %d as expected", errno); + } + + exp_errno = expected_errno(event->response); + event++; + + exp_ret = exp_errno ? -1 : 1; + errno = 0; + if (write(fd, addr, 1) != exp_ret || errno != exp_errno) { + tst_res(TFAIL, "write() got errno %d (expected %d)", errno, exp_errno); + exit(3); + } else if (errno == exp_errno) { + tst_res(TINFO, "write() got errno %d as expected", errno); + } + + SAFE_LSEEK(fd, 0, SEEK_SET); + + exp_errno = expected_errno(event->response); + event++; + + exp_ret = exp_errno ? -1 : BUF_SIZE; + errno = 0; + if (read(fd, buf, BUF_SIZE) != exp_ret || errno != exp_errno) { + tst_res(TFAIL, "read() got errno %d (expected %d)", errno, exp_errno); + exit(4); + } else if (errno == exp_errno) { + tst_res(TINFO, "read() got errno %d as expected", errno); + } + + SAFE_CLOSE(fd); + + exp_errno = expected_errno(event->response); + event++; + + /* + * If execve() is allowed by permission events, check if executing a + * file that open for write is allowed. + * HSM needs to be able to write to file during pre-content event, so it + * requires that a file being executed can be open for write, which also + * means that a file open for write can be executed. + * Therefore, ETXTBSY is to be expected when file is not being watched + * at all or being watched but not with pre-content events in mask. + */ + if (!exp_errno) { + fd = SAFE_OPEN(FILE_EXEC_PATH, O_RDWR); + if (!tc->event_set[0].mask) + exp_errno = ETXTBSY; + } + + exp_ret = exp_errno ? -1 : 0; + errno = 0; + if (execve(FILE_EXEC_PATH, argv, environ) != exp_ret || errno != exp_errno) { + tst_res(TFAIL, "execve() got errno %d (expected %d)", errno, exp_errno); + exit(5); + } else if (errno == exp_errno) { + tst_res(TINFO, "execve() got errno %d as expected", errno); + } + + if (fd >= 0) + SAFE_CLOSE(fd); +} + +static void child_handler(int tmp) +{ + (void)tmp; + /* + * Close notification fd so that we cannot block while reading + * from it + */ + SAFE_CLOSE(fd_notify); + fd_notify = -1; +} + +static void run_child(struct tcase *tc) +{ + struct sigaction child_action; + + child_action.sa_handler = child_handler; + sigemptyset(&child_action.sa_mask); + child_action.sa_flags = SA_NOCLDSTOP; + + if (sigaction(SIGCHLD, &child_action, NULL) < 0) { + tst_brk(TBROK | TERRNO, + "sigaction(SIGCHLD, &child_action, NULL) failed"); + } + + child_pid = SAFE_FORK(); + + if (child_pid == 0) { + /* Child will generate events now */ + SAFE_CLOSE(fd_notify); + generate_events(tc); + exit(0); + } +} + +static void check_child(void) +{ + struct sigaction child_action; + int child_ret; + + child_action.sa_handler = SIG_IGN; + sigemptyset(&child_action.sa_mask); + child_action.sa_flags = SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &child_action, NULL) < 0) { + tst_brk(TBROK | TERRNO, + "sigaction(SIGCHLD, &child_action, NULL) failed"); + } + SAFE_WAITPID(-1, &child_ret, 0); + + if (WIFEXITED(child_ret) && WEXITSTATUS(child_ret) == 0) + tst_res(TPASS, "child exited correctly"); + else + tst_res(TFAIL, "child %s", tst_strstatus(child_ret)); +} + +static int setup_mark(unsigned int n) +{ + unsigned int i = 0; + struct tcase *tc = &tcases[n]; + struct fanotify_mark_type *mark = &tc->mark; + char *const files[] = {fname, FILE_EXEC_PATH}; + + tst_res(TINFO, "Test #%d: %s", n, tc->tname); + + fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_PRE_CONTENT, O_RDONLY); + + if (mark->flag == FAN_MARK_PARENT) { + SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD | mark->flag, + tc->mask, AT_FDCWD, MOUNT_PATH); + return 0; + } + + for (; i < ARRAY_SIZE(files); i++) { + SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD | mark->flag, + tc->mask, AT_FDCWD, files[i]); + } + + return 0; +} + +static void test_fanotify(unsigned int n) +{ + int ret, len = 0, i = 0, test_num = 0; + struct tcase *tc = &tcases[n]; + struct event *event_set = tc->event_set; + + if (setup_mark(n) != 0) + return; + + run_child(tc); + + /* + * Process events + * + * even if we do not expect another event, let read() wait for child + * process to exit and accomodate for multiple access events + */ + while (test_num < EVENT_SET_MAX && fd_notify != -1) { + struct fanotify_event_metadata *event; + struct fanotify_event_info_range *range; + + if (i == len) { + /* Get more events */ + ret = read(fd_notify, event_buf + len, + EVENT_BUF_LEN - len); + /* Received SIGCHLD */ + if (fd_notify == -1) + break; + if (ret < 0) { + tst_brk(TBROK, + "read(%d, buf, %zu) failed", + fd_notify, EVENT_BUF_LEN); + } + len += ret; + } + + /* + * If we got an event after the last event and the last event was + * allowed then assume this is another event of the same type. + * This is to accomodate for the fact that a single read() may + * generate an unknown number of access permission events if they + * are allowed. + */ + if (test_num > 0 && !event_set[test_num].mask && + event_set[test_num-1].response == FAN_ALLOW) + test_num--; + + event = (struct fanotify_event_metadata *)&event_buf[i]; + range = (struct fanotify_event_info_range *)(event + 1); + /* Permission events cannot be merged, so the event mask + * reported should exactly match the event mask within the + * event set. + */ + if (event->mask != event_set[test_num].mask) { + tst_res(TFAIL, + "got event: mask=%llx (expected %llx) " + "pid=%u fd=%d", + (unsigned long long)event->mask, + event_set[test_num].mask, + (unsigned int)event->pid, event->fd); + } else if (event->pid != child_pid) { + tst_res(TFAIL, + "got event: mask=%llx pid=%u " + "(expected %u) fd=%d", + (unsigned long long)event->mask, + (unsigned int)event->pid, + (unsigned int)child_pid, + event->fd); + } else if (event->mask & LTP_PRE_CONTENT_EVENTS) { + if (event->event_len < sizeof(*event) + sizeof(*range) || + range->hdr.info_type != FAN_EVENT_INFO_TYPE_RANGE) { + tst_res(TFAIL, + "got event: mask=%llx pid=%u len=%d fd=%d " + "(expected range info)", + (unsigned long long)event->mask, + (unsigned int)event->pid, + (unsigned int)event->event_len, + event->fd); + } else { + tst_res(TPASS, + "got event: mask=%llx pid=%u fd=%d " + "offset=%llu count=%llu", + (unsigned long long)event->mask, + (unsigned int)event->pid, event->fd, + range->offset, range->count); + } + } else { + tst_res(TPASS, + "got event: mask=%llx pid=%u fd=%d", + (unsigned long long)event->mask, + (unsigned int)event->pid, event->fd); + } + + /* Write response to the permission event */ + if (event_set[test_num].mask & + (LTP_ALL_PERM_EVENTS | LTP_PRE_CONTENT_EVENTS)) { + struct fanotify_response resp; + + resp.fd = event->fd; + resp.response = event_set[test_num].response; + SAFE_WRITE(SAFE_WRITE_ALL, fd_notify, &resp, sizeof(resp)); + tst_res(TPASS, "response=%x fd=%d", resp.response, resp.fd); + } + + i += event->event_len; + + if (event->fd != FAN_NOFD) { + char c; + + /* Verify that read from event fd does not generate events */ + SAFE_READ(0, event->fd, &c, 1); + SAFE_CLOSE(event->fd); + } + + test_num++; + } + + for (; event_set[test_num].mask && test_num < EVENT_SET_MAX; test_num++) { + tst_res(TFAIL, "didn't get event: mask=%llx", + event_set[test_num].mask); + + } + + check_child(); + + if (fd_notify > 0) + SAFE_CLOSE(fd_notify); +} + +static void setup(void) +{ + page_sz = getpagesize(); + + sprintf(fname, MOUNT_PATH"/fname_%d", getpid()); + SAFE_FILE_PRINTF(fname, "1"); + SAFE_TRUNCATE(fname, page_sz*101); + + REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(FAN_CLASS_PRE_CONTENT, FAN_MARK_FILESYSTEM, + FAN_PRE_ACCESS, fname); + + SAFE_CP(TEST_APP, FILE_EXEC_PATH); +} + +static void cleanup(void) +{ + if (fd_notify > 0) + SAFE_CLOSE(fd_notify); +} + +static const char *const resource_files[] = { + TEST_APP, + NULL +}; + +static struct tst_test test = { + .timeout = 1, + .test = test_fanotify, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .needs_root = 1, + .mount_device = 1, + .all_filesystems = 1, + .mntpoint = MOUNT_PATH, + .resource_files = resource_files +}; + +#else + TST_TEST_TCONF("system doesn't have required fanotify support"); +#endif diff --git a/testcases/kernel/syscalls/fanotify/fanotify_child.c b/testcases/kernel/syscalls/fanotify/fanotify_child.c index 2e4e189c..73f63451 100755 --- a/testcases/kernel/syscalls/fanotify/fanotify_child.c +++ b/testcases/kernel/syscalls/fanotify/fanotify_child.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved. * diff --git a/testcases/kernel/syscalls/fchmod/fchmod01.c b/testcases/kernel/syscalls/fchmod/fchmod01.c index 05b56679..e23dcded 100755 --- a/testcases/kernel/syscalls/fchmod/fchmod01.c +++ b/testcases/kernel/syscalls/fchmod/fchmod01.c @@ -2,9 +2,10 @@ /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * Author: Wayne Boyer and William Roske - * - * Test Description: - * fchmod() will succeed to change the mode permissions of a file specified + */ + +/*\ + * Verify that fchmod() can succeed to change the mode permissions of a file specified * by file descriptor. */ diff --git a/testcases/kernel/syscalls/fchmod/fchmod02.c b/testcases/kernel/syscalls/fchmod/fchmod02.c index d6abeffc..e25739bc 100755 --- a/testcases/kernel/syscalls/fchmod/fchmod02.c +++ b/testcases/kernel/syscalls/fchmod/fchmod02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that, fchmod(2) will succeed to change the mode of a file/directory * set the sticky bit on it if invoked by root (uid = 0) process with * the following constraints: diff --git a/testcases/kernel/syscalls/fchmod/fchmod03.c b/testcases/kernel/syscalls/fchmod/fchmod03.c index bdd720c3..028ec155 100755 --- a/testcases/kernel/syscalls/fchmod/fchmod03.c +++ b/testcases/kernel/syscalls/fchmod/fchmod03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that, fchmod(2) will succeed to change the mode of a file * and set the sticky bit on it if invoked by non-root (uid != 0) * process with the following constraints: diff --git a/testcases/kernel/syscalls/fchmod/fchmod04.c b/testcases/kernel/syscalls/fchmod/fchmod04.c index 4fa69e22..52768702 100755 --- a/testcases/kernel/syscalls/fchmod/fchmod04.c +++ b/testcases/kernel/syscalls/fchmod/fchmod04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that, fchmod(2) will succeed to change the mode of a directory * and set the sticky bit on it if invoked by non-root (uid != 0) process * with the following constraints: diff --git a/testcases/kernel/syscalls/fchmod/fchmod05.c b/testcases/kernel/syscalls/fchmod/fchmod05.c index 0c731d60..04f82165 100755 --- a/testcases/kernel/syscalls/fchmod/fchmod05.c +++ b/testcases/kernel/syscalls/fchmod/fchmod05.c @@ -2,18 +2,16 @@ /* * Copyright (c) International Business Machines Corp., 2001 * Author: Wayne Boyer + */ + +/*\ + * Verify that, fchmod(2) will succeed to change the mode of a directory + * but fails to set the setgid bit on it if invoked by non-root (uid != 0) + * process with the following constraints: * - * Test Description: - * Verify that, fchmod(2) will succeed to change the mode of a directory - * but fails to set the setgid bit on it if invoked by non-root (uid != 0) - * process with the following constraints, - * - the process is the owner of the directory. - * - the effective group ID or one of the supplementary group ID's of the - * process is not equal to the group ID of the directory. - * - * Expected Result: - * fchmod() should return value 0 on success and though succeeds to change - * the mode of a directory but fails to set setgid bit on it. + * - The process is the owner of the directory. + * - The effective group ID or one of the supplementary group ID's of the + * process is not equal to the group ID of the directory. */ #include @@ -58,13 +56,8 @@ static void setup(void) SAFE_MKDIR(TESTDIR, DIR_MODE); - if (setgroups(1, <puser->pw_gid) == -1) { - tst_brk(TBROK, "Couldn't change supplementary group Id: %s", - tst_strerrno(TST_ERR)); - } - + SAFE_SETGROUPS(1, <puser->pw_gid); SAFE_CHOWN(TESTDIR, ltpuser->pw_uid, free_gid); - SAFE_SETEGID(ltpuser->pw_gid); SAFE_SETEUID(ltpuser->pw_uid); diff --git a/testcases/kernel/syscalls/fchmod/fchmod06.c b/testcases/kernel/syscalls/fchmod/fchmod06.c index 4a8aee6c..458239a2 100755 --- a/testcases/kernel/syscalls/fchmod/fchmod06.c +++ b/testcases/kernel/syscalls/fchmod/fchmod06.c @@ -3,14 +3,19 @@ * Copyright (c) International Business Machines Corp., 2001 * Author: Wayne Boyer * Copyright (c) 2018 Cyril Hrubis - */ -/* - * Test that fchmod() fails and sets the proper errno values. + * Copyright (c) Linux Test Project, 2001-2025 */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif +/*\ + * Verify that :man2:`fchmod` fails and sets the proper errno values: + * + * - EPERM -- the effective UID does not match the owner of the file, and the process is not privileged + * - EBADF -- file descriptor was closed + * - EROFS -- file descriptor opened as a read-only + */ + +#define _GNU_SOURCE + #include #include #include @@ -36,22 +41,7 @@ static void verify_fchmod(unsigned int i) { struct tcase *tc = &tcases[i]; - TEST(fchmod(*tc->fd, tc->mode)); - - if (TST_RET != -1) { - tst_res(TFAIL, "fchmod() passed unexpectedly (%li)", - TST_RET); - return; - } - - if (TST_ERR == tcases[i].exp_errno) { - tst_res(TPASS | TTERRNO, "fchmod() failed expectedly"); - return; - } - - tst_res(TFAIL | TTERRNO, - "fchmod() failed unexpectedly, expected %i - %s", - TST_ERR, tst_strerrno(TST_ERR)); + TST_EXP_FAIL(fchmod(*tc->fd, tc->mode), tc->exp_errno); } static void setup(void) diff --git a/testcases/kernel/syscalls/fchmodat/.gitignore b/testcases/kernel/syscalls/fchmodat/.gitignore index a9508bc5..09d5c47d 100755 --- a/testcases/kernel/syscalls/fchmodat/.gitignore +++ b/testcases/kernel/syscalls/fchmodat/.gitignore @@ -1 +1,2 @@ /fchmodat01 +/fchmodat02 diff --git a/testcases/kernel/syscalls/fchmodat/fchmodat01.c b/testcases/kernel/syscalls/fchmodat/fchmodat01.c index 3deff0eb..672fbff5 100755 --- a/testcases/kernel/syscalls/fchmodat/fchmodat01.c +++ b/testcases/kernel/syscalls/fchmodat/fchmodat01.c @@ -1,99 +1,77 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2006 - * + * Copyright (c) Linux Test Project, 2003-2023 * 08/28/2006 AUTHOR: Yi Yang */ /*\ - * [Description] + * Check the basic functionality of the fchmodat() system call. * - * This test case will verify basic function of fchmodat. + * - fchmodat() passes if dir_fd is file descriptor to the directory + * where the file is located and pathname is relative path of the file. + * - fchmodat() passes if pathname is absolute, then dirfd is ignored. + * - fchmodat() passes if dir_fd is AT_FDCWD and pathname is interpreted + * relative to the current working directory of the calling process. */ -#define _GNU_SOURCE - -#include -#include #include #include #include "tst_test.h" -#include "lapi/syscalls.h" -#ifndef AT_FDCWD -#define AT_FDCWD -100 -#endif +#define TESTDIR "fchmodatdir" +#define TESTFILE "fchmodatfile" +#define FILEPATH "fchmodatdir/fchmodatfile" -static char pathname[256]; -static char testfile[256]; -static char testfile2[256]; -static char testfile3[256]; +static int dir_fd, file_fd; +static int atcwd_fd = AT_FDCWD; +static char *abs_path; +static char *test_file; +static char *file_path; static struct tcase { - int exp_errno; - char *exp_errval; + int *fd; + char **filenames; + char **full_path; } tcases[] = { - { 0, NULL}, - { 0, NULL}, - { ENOTDIR, "ENOTDIR"}, - { EBADF, "EBADF"}, - { 0, NULL}, - { 0, NULL}, + {&dir_fd, &test_file, &file_path}, + {&file_fd, &abs_path, &abs_path}, + {&atcwd_fd, &file_path, &file_path}, }; -static int fds[ARRAY_SIZE(tcases)]; -static char *filenames[ARRAY_SIZE(tcases)]; static void verify_fchmodat(unsigned int i) { struct tcase *tc = &tcases[i]; + struct stat st; - if (tc->exp_errno == 0) - TST_EXP_PASS(tst_syscall(__NR_fchmodat, fds[i], filenames[i], 0600), - "fchmodat() returned the expected errno %d: %s", - TST_ERR, strerror(TST_ERR)); + TST_EXP_PASS(fchmodat(*tc->fd, *tc->filenames, 0600, 0), + "fchmodat(%d, %s, 0600, 0)", + *tc->fd, *tc->filenames); + + SAFE_LSTAT(*tc->full_path, &st); + + if ((st.st_mode & ~S_IFREG) == 0600) + tst_res(TPASS, "File permission changed correctly"); else - TST_EXP_FAIL(tst_syscall(__NR_fchmodat, fds[i], filenames[i], 0600), - tc->exp_errno, - "fchmodat() returned the expected errno %d: %s", - TST_ERR, strerror(TST_ERR)); + tst_res(TFAIL, "File permission not changed correctly"); } static void setup(void) { - /* Initialize test dir and file names */ - char *abs_path = tst_get_tmpdir(); - int p = getpid(); + abs_path = tst_tmpdir_genpath(FILEPATH); - sprintf(pathname, "fchmodattestdir%d", p); - sprintf(testfile, "fchmodattest%d.txt", p); - sprintf(testfile2, "%s/fchmodattest%d.txt", abs_path, p); - sprintf(testfile3, "fchmodattestdir%d/fchmodattest%d.txt", p, p); - - free(abs_path); - - SAFE_MKDIR(pathname, 0700); - - fds[0] = SAFE_OPEN(pathname, O_DIRECTORY); - fds[1] = fds[4] = fds[0]; - - SAFE_FILE_PRINTF(testfile, "%s", testfile); - SAFE_FILE_PRINTF(testfile2, "%s", testfile2); - - fds[2] = SAFE_OPEN(testfile3, O_CREAT | O_RDWR, 0600); - fds[3] = 100; - fds[5] = AT_FDCWD; - - filenames[0] = filenames[2] = filenames[3] = filenames[4] = testfile; - filenames[1] = testfile2; - filenames[5] = testfile3; + SAFE_MKDIR(TESTDIR, 0700); + dir_fd = SAFE_OPEN(TESTDIR, O_DIRECTORY); + file_fd = SAFE_OPEN(FILEPATH, O_CREAT | O_RDWR, 0600); } static void cleanup(void) { - if (fds[0] > 0) - close(fds[0]); - if (fds[2] > 0) - close(fds[2]); + if (dir_fd > -1) + SAFE_CLOSE(dir_fd); + + if (file_fd > -1) + SAFE_CLOSE(file_fd); } static struct tst_test test = { @@ -101,5 +79,10 @@ static struct tst_test test = { .test = verify_fchmodat, .setup = setup, .cleanup = cleanup, + .bufs = (struct tst_buffers []) { + {&test_file, .str = TESTFILE}, + {&file_path, .str = FILEPATH}, + {}, + }, .needs_tmpdir = 1, }; diff --git a/testcases/kernel/syscalls/fchmodat/fchmodat02.c b/testcases/kernel/syscalls/fchmodat/fchmodat02.c new file mode 100644 index 00000000..b8715463 --- /dev/null +++ b/testcases/kernel/syscalls/fchmodat/fchmodat02.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Linux Test Project, 2003-2023 + * Author: Yi Yang + */ + +/*\ + * Tests basic error handling of the fchmodat() syscall. + * + * - fchmodat() fails with ENOTDIR if dir_fd is file descriptor + * to the file and pathname is relative path of the file. + * - fchmodat() fails with EBADF if dir_fd is invalid. + * - fchmodat() fails with EFAULT if pathname points outside + * the accessible address space. + * - fchmodat() fails with ENAMETOOLONG if path is too long. + * - fchmodat() fails with ENOENT if pathname does not exist. + * - fchmodat() fails with EINVAL if flag is invalid. + */ + +#include +#include +#include "tst_test.h" + +#define TESTFILE "fchmodatfile" + +static int file_fd; +static int bad_fd = -1; +static char path[PATH_MAX + 2]; +static char *long_path = path; +static int fd_atcwd = AT_FDCWD; +static char *bad_path; +static char *test_path; +static char *empty_path; + +static struct tcase { + int *fd; + char **filenames; + int flag; + int exp_errno; + const char *desc; +} tcases[] = { + {&file_fd, &test_path, 0, ENOTDIR, "fd pointing to file"}, + {&bad_fd, &test_path, 0, EBADF, "invalid fd"}, + {&file_fd, &bad_path, 0, EFAULT, "invalid address"}, + {&file_fd, &long_path, 0, ENAMETOOLONG, "pathname too long"}, + {&file_fd, &empty_path, 0, ENOENT, "path is empty"}, + {&fd_atcwd, &test_path, -1, EINVAL, "invalid flag"}, +}; + +static void verify_fchmodat(unsigned int i) +{ + struct tcase *tc = &tcases[i]; + + TST_EXP_FAIL(fchmodat(*tc->fd, *tc->filenames, 0600, tc->flag), + tc->exp_errno, "fchmodat() with %s", tc->desc); +} + +static void setup(void) +{ + file_fd = SAFE_OPEN(TESTFILE, O_CREAT | O_RDWR, 0600); + + bad_path = tst_get_bad_addr(NULL); + + memset(path, 'a', PATH_MAX + 2); +} + +static void cleanup(void) +{ + if (file_fd > -1) + SAFE_CLOSE(file_fd); +} + +static struct tst_test test = { + .test = verify_fchmodat, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .bufs = (struct tst_buffers []) { + {&test_path, .str = TESTFILE}, + {&empty_path, .str = ""}, + {}, + }, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/fchmodat2/.gitignore b/testcases/kernel/syscalls/fchmodat2/.gitignore new file mode 100644 index 00000000..9f713198 --- /dev/null +++ b/testcases/kernel/syscalls/fchmodat2/.gitignore @@ -0,0 +1,2 @@ +fchmodat2_01 +fchmodat2_02 diff --git a/testcases/kernel/syscalls/fchmodat2/Makefile b/testcases/kernel/syscalls/fchmodat2/Makefile new file mode 100644 index 00000000..8cf1b902 --- /dev/null +++ b/testcases/kernel/syscalls/fchmodat2/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/fchmodat2/fchmodat2_01.c b/testcases/kernel/syscalls/fchmodat2/fchmodat2_01.c new file mode 100644 index 00000000..dcf67cd5 --- /dev/null +++ b/testcases/kernel/syscalls/fchmodat2/fchmodat2_01.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that fchmodat2() syscall is properly working with + * regular files, symbolic links and directories. AT_SYMLINK_NOFOLLOW is a + * special feature that is blocked by VFS since 5d1f903f75a8 and any of its + * usage on symlinks will raise EOPNOTSUPP. + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "tst_safe_file_at.h" +#include "lapi/fcntl.h" +#include "lapi/stat.h" + +#define MNTPOINT "mntpoint" +#define FNAME "myfile" +#define SNAME "symlink" +#define DNAME "mydir" +#define DNAME_PATH MNTPOINT"/"DNAME + +static int fd_dir = -1; + +static void verify_mode(int dirfd, const char *path, mode_t mode) +{ + struct stat st; + + SAFE_FSTATAT(dirfd, path, &st, AT_SYMLINK_NOFOLLOW); + TST_EXP_EQ_LI(st.st_mode, mode); +} + +static void test_regular_file(void) +{ + tst_res(TINFO, "Using regular files"); + + SAFE_CHMOD(MNTPOINT"/"FNAME, 0640); + + SAFE_FCHMODAT2(fd_dir, FNAME, 0700, 0); + verify_mode(fd_dir, FNAME, S_IFREG | 0700); + + SAFE_FCHMODAT2(fd_dir, FNAME, 0700, AT_SYMLINK_NOFOLLOW); + verify_mode(fd_dir, FNAME, S_IFREG | 0700); +} + +static void test_symbolic_link(void) +{ + tst_res(TINFO, "Using symbolic link"); + + SAFE_FCHMODAT2(fd_dir, SNAME, 0700, 0); + verify_mode(fd_dir, FNAME, S_IFREG | 0700); + verify_mode(fd_dir, SNAME, S_IFLNK | 0777); + + TST_EXP_FAIL(tst_syscall(__NR_fchmodat2, + fd_dir, SNAME, 0640, AT_SYMLINK_NOFOLLOW), + EOPNOTSUPP); +} + +static void test_empty_folder(void) +{ + tst_res(TINFO, "Using empty folder"); + + int fd; + + SAFE_CHMOD(DNAME_PATH, 0640); + fd = SAFE_OPEN(DNAME_PATH, O_PATH | O_DIRECTORY, 0640); + + SAFE_FCHMODAT2(fd, "", 0777, AT_EMPTY_PATH); + verify_mode(fd_dir, DNAME, S_IFDIR | 0777); + + SAFE_CLOSE(fd); +} + +static void run(void) +{ + test_regular_file(); + test_empty_folder(); + test_symbolic_link(); +} + +static void setup(void) +{ + fd_dir = SAFE_OPEN(MNTPOINT, O_PATH | O_DIRECTORY, 0640); + + if (access(DNAME_PATH, F_OK) == -1) + SAFE_MKDIR(DNAME_PATH, 0640); + + SAFE_TOUCH(MNTPOINT"/"FNAME, 0640, NULL); + SAFE_SYMLINKAT(FNAME, fd_dir, SNAME); +} + +static void cleanup(void) +{ + SAFE_UNLINKAT(fd_dir, SNAME, 0); + SAFE_RMDIR(DNAME_PATH); + + if (fd_dir != -1) + SAFE_CLOSE(fd_dir); +} + +static struct tst_test test = { + .timeout = 9, + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .mntpoint = MNTPOINT, + .format_device = 1, + .all_filesystems = 1, + .tags = (const struct tst_tag[]) { + {"linux-git", "5d1f903f75a8"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/fchmodat2/fchmodat2_02.c b/testcases/kernel/syscalls/fchmodat2/fchmodat2_02.c new file mode 100644 index 00000000..0da65f40 --- /dev/null +++ b/testcases/kernel/syscalls/fchmodat2/fchmodat2_02.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that fchmodat2() syscall properly raises errors with + * invalid values. + */ + +#include "tst_test.h" +#include "lapi/fcntl.h" +#include "lapi/syscalls.h" +#include "tst_tmpdir.h" + +#define FILENAME "file.bin" + +static char *tmpdir; +static int fd; +static int fd_invalid = -1; + +static struct tcase { + int *fd; + char *fname; + int mode; + int flag; + int exp_errno; + char *msg; +} tcases[] = { + {&fd_invalid, FILENAME, 0777, 0, EBADF, "bad file descriptor"}, + {&fd, "doesnt_exist.txt", 0777, 0, ENOENT, "unexisting file"}, + {&fd, FILENAME, 0777, -1, EINVAL, "invalid flags"}, +}; + +static void run(unsigned int i) +{ + struct tcase *tc = &tcases[i]; + + TST_EXP_FAIL(tst_syscall(__NR_fchmodat2, + *tc->fd, tc->fname, tc->mode, tc->flag), + tc->exp_errno, + "Test %s", tc->msg); +} + +static void setup(void) +{ + tmpdir = tst_tmpdir_path(); + + SAFE_TOUCH(FILENAME, 0640, NULL); + fd = SAFE_OPEN(tmpdir, O_PATH | O_DIRECTORY, 0640); +} + +static void cleanup(void) +{ + if (fd != -1) + SAFE_CLOSE(fd); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, +}; + diff --git a/testcases/kernel/syscalls/fchown/fchown01.c b/testcases/kernel/syscalls/fchown/fchown01.c index 4ab5a2f3..946cff0c 100755 --- a/testcases/kernel/syscalls/fchown/fchown01.c +++ b/testcases/kernel/syscalls/fchown/fchown01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Basic test for fchown(). Call fchown() on a fd and expect it to pass. */ diff --git a/testcases/kernel/syscalls/fchown/fchown02.c b/testcases/kernel/syscalls/fchown/fchown02.c index bd1baf3b..6d6c4c17 100755 --- a/testcases/kernel/syscalls/fchown/fchown02.c +++ b/testcases/kernel/syscalls/fchown/fchown02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that fchown(2) invoked by super-user: * * - clears setuid and setgid bits set on an executable file diff --git a/testcases/kernel/syscalls/fchown/fchown03.c b/testcases/kernel/syscalls/fchown/fchown03.c index 97d6c9d3..cda3234f 100755 --- a/testcases/kernel/syscalls/fchown/fchown03.c +++ b/testcases/kernel/syscalls/fchown/fchown03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, fchown(2) succeeds to change the group of a file specified * by path when called by non-root user with the following constraints: * diff --git a/testcases/kernel/syscalls/fchown/fchown04.c b/testcases/kernel/syscalls/fchown/fchown04.c index a7af3aae..56b91304 100755 --- a/testcases/kernel/syscalls/fchown/fchown04.c +++ b/testcases/kernel/syscalls/fchown/fchown04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that: * * 1. fchown() returns -1 and sets errno to EPERM if the effective user id diff --git a/testcases/kernel/syscalls/fchown/fchown05.c b/testcases/kernel/syscalls/fchown/fchown05.c index 47ee1712..97992565 100755 --- a/testcases/kernel/syscalls/fchown/fchown05.c +++ b/testcases/kernel/syscalls/fchown/fchown05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, fchown() succeeds to change the owner and group of a file * specified by file descriptor to any numeric owner(uid)/group(gid) values * when invoked by super-user. diff --git a/testcases/kernel/syscalls/fchownat/.gitignore b/testcases/kernel/syscalls/fchownat/.gitignore index 35c00345..60fac7e6 100755 --- a/testcases/kernel/syscalls/fchownat/.gitignore +++ b/testcases/kernel/syscalls/fchownat/.gitignore @@ -1,2 +1,3 @@ /fchownat01 /fchownat02 +/fchownat03 diff --git a/testcases/kernel/syscalls/fchownat/fchownat.h b/testcases/kernel/syscalls/fchownat/fchownat.h deleted file mode 100755 index 927cf929..00000000 --- a/testcases/kernel/syscalls/fchownat/fchownat.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2014 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 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 Library 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. - * - */ - -#ifndef FCHOWNAT_H -#define FCHOWNAT_H - -#include -#include "config.h" -#include "lapi/syscalls.h" - - -#if !defined(HAVE_FCHOWNAT) -static inline int fchownat(int dirfd, const char *filename, uid_t owner, - gid_t group, int flags) -{ - return tst_syscall(__NR_fchownat, dirfd, filename, owner, group, flags); -} -#endif - - -#endif /* FCHOWNAT_H */ diff --git a/testcases/kernel/syscalls/fchownat/fchownat01.c b/testcases/kernel/syscalls/fchownat/fchownat01.c index 3b29f1e7..5c0aa1a8 100755 --- a/testcases/kernel/syscalls/fchownat/fchownat01.c +++ b/testcases/kernel/syscalls/fchownat/fchownat01.c @@ -1,134 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2006 - * AUTHOR: Yi Yang - * - * 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 + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Linux Test Project, 2007-2025 */ -/* - * DESCRIPTION - * This test case will verify basic function of fchownat - * added by kernel 2.6.16 or up. + +/*\ + * Verify that fchownat() succeeds to change file's ownership if the file + * descriptor is AT_FDCWD or set by opening a directory. */ #define _GNU_SOURCE +#include "tst_test.h" -#include -#include -#include -#include -#include -#include -#include +#define TESTFILE1 "testfile1" +#define TESTFILE2 "testfile2" -#include "test.h" -#include "safe_macros.h" -#include "fchownat.h" -#include "lapi/fcntl.h" +static uid_t set_uid = 1000; +static gid_t set_gid = 1000; +static int dir_fd = -1; -#define TESTFILE "testfile" - -static void setup(void); -static void cleanup(void); - -static int dir_fd; -static int fd; -static int no_fd = -1; -static int cu_fd = AT_FDCWD; - -static struct test_case_t { - int exp_ret; - int exp_errno; - int flag; - int *fds; - char *filenames; -} test_cases[] = { - {0, 0, 0, &dir_fd, TESTFILE}, - {-1, ENOTDIR, 0, &fd, TESTFILE}, - {-1, EBADF, 0, &no_fd, TESTFILE}, - {-1, EINVAL, 9999, &dir_fd, TESTFILE}, - {0, 0, 0, &cu_fd, TESTFILE}, -}; - -char *TCID = "fchownat01"; -int TST_TOTAL = ARRAY_SIZE(test_cases); -static void fchownat_verify(const struct test_case_t *); - -int main(int ac, char **av) +static void fchownat_verify(void) { - int lc; - int i; + struct stat stat_buf; - tst_parse_opts(ac, av, NULL, NULL); + TST_EXP_PASS(fchownat(AT_FDCWD, TESTFILE1, set_uid, set_gid, 0), + "fchownat(%d, %s, %d, %d, 0)", + AT_FDCWD, TESTFILE1, set_uid, set_gid); - setup(); + SAFE_STAT(TESTFILE1, &stat_buf); + TST_EXP_EQ_LI(stat_buf.st_uid, set_uid); + TST_EXP_EQ_LI(stat_buf.st_gid, set_gid); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - for (i = 0; i < TST_TOTAL; i++) - fchownat_verify(&test_cases[i]); - } + TST_EXP_PASS(fchownat(dir_fd, TESTFILE2, set_uid, set_gid, 0), + "fchownat(%d, %s, %d, %d, 0)", + dir_fd, TESTFILE2, set_uid, set_gid); - cleanup(); - tst_exit(); + SAFE_STAT(TESTFILE2, &stat_buf); + TST_EXP_EQ_LI(stat_buf.st_uid, set_uid); + TST_EXP_EQ_LI(stat_buf.st_gid, set_gid); } static void setup(void) { - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - dir_fd = SAFE_OPEN(cleanup, "./", O_DIRECTORY); - - SAFE_TOUCH(cleanup, TESTFILE, 0600, NULL); - - fd = SAFE_OPEN(cleanup, "testfile2", O_CREAT | O_RDWR, 0600); -} - -static void fchownat_verify(const struct test_case_t *test) -{ - TEST(fchownat(*(test->fds), test->filenames, geteuid(), - getegid(), test->flag)); - - if (TEST_RETURN != test->exp_ret) { - tst_resm(TFAIL | TTERRNO, - "fchownat() returned %ld, expected %d, errno=%d", - TEST_RETURN, test->exp_ret, test->exp_errno); - return; - } - - if (TEST_ERRNO == test->exp_errno) { - tst_resm(TPASS | TTERRNO, - "fchownat() returned the expected errno %d: %s", - test->exp_ret, strerror(test->exp_errno)); - } else { - tst_resm(TFAIL | TTERRNO, - "fchownat() failed unexpectedly; expected: %d - %s", - test->exp_errno, strerror(test->exp_errno)); - } + dir_fd = SAFE_OPEN("./", O_DIRECTORY); + SAFE_TOUCH(TESTFILE1, 0600, NULL); + SAFE_TOUCH(TESTFILE2, 0600, NULL); } static void cleanup(void) { - if (fd > 0) - close(fd); - - if (dir_fd > 0) - close(dir_fd); - - tst_rmdir(); + if (dir_fd != -1) + SAFE_CLOSE(dir_fd); } + +static struct tst_test test = { + .needs_tmpdir = 1, + .needs_root = 1, + .test_all = fchownat_verify, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/fchownat/fchownat02.c b/testcases/kernel/syscalls/fchownat/fchownat02.c index c39b0a91..e2e4c376 100755 --- a/testcases/kernel/syscalls/fchownat/fchownat02.c +++ b/testcases/kernel/syscalls/fchownat/fchownat02.c @@ -1,136 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014 Fujitsu Ltd. - * Author: Zeng Linggang - * - * 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. + * Copyright (c) Linux Test Project, 2014-2025 */ -/* - * Test Description: - * Verify that, - * The flag of fchownat() is AT_SYMLINK_NOFOLLOW and the pathname would - * not be dereferenced if the pathname is a symbolic link. + +/*\ + * Verify that fchownat() will operate on symbolic links when + * AT_SYMLINK_NOFOLLOW is used. */ #define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" -#include "fchownat.h" -#include "lapi/fcntl.h" +#include "tst_test.h" #define TESTFILE "testfile" #define TESTFILE_LINK "testfile_link" -char *TCID = "fchownat02"; -int TST_TOTAL = 1; - -static int dir_fd; static uid_t set_uid = 1000; static gid_t set_gid = 1000; -static void setup(void); -static void cleanup(void); -static void test_verify(void); -static void fchownat_verify(void); - -int main(int ac, char **av) -{ - int lc; - int i; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - for (i = 0; i < TST_TOTAL; i++) - fchownat_verify(); - } - - cleanup(); - tst_exit(); -} static void setup(void) { struct stat c_buf, l_buf; - tst_require_root(); - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - dir_fd = SAFE_OPEN(cleanup, "./", O_DIRECTORY); - - SAFE_TOUCH(cleanup, TESTFILE, 0600, NULL); - - SAFE_SYMLINK(cleanup, TESTFILE, TESTFILE_LINK); - - SAFE_STAT(cleanup, TESTFILE_LINK, &c_buf); - - SAFE_LSTAT(cleanup, TESTFILE_LINK, &l_buf); + SAFE_TOUCH(TESTFILE, 0600, NULL); + SAFE_SYMLINK(TESTFILE, TESTFILE_LINK); + SAFE_STAT(TESTFILE_LINK, &c_buf); + SAFE_LSTAT(TESTFILE_LINK, &l_buf); if (l_buf.st_uid == set_uid || l_buf.st_gid == set_gid) { - tst_brkm(TBROK | TERRNO, cleanup, - "link_uid(%d) == set_uid(%d) or link_gid(%d) == " - "set_gid(%d)", l_buf.st_uid, set_uid, l_buf.st_gid, - set_gid); + tst_brk(TBROK, + "uid link(%d) == set(%d) or gid link(%d) == set(%d)", + l_buf.st_uid, set_uid, l_buf.st_gid, set_gid); } } -static void fchownat_verify(void) -{ - TEST(fchownat(dir_fd, TESTFILE_LINK, set_uid, set_gid, - AT_SYMLINK_NOFOLLOW)); - - if (TEST_RETURN != 0) { - tst_resm(TFAIL | TTERRNO, "fchownat() failed, errno=%d : %s", - TEST_ERRNO, strerror(TEST_ERRNO)); - } else { - test_verify(); - } -} - -static void test_verify(void) +static void run(void) { struct stat c_buf, l_buf; - SAFE_STAT(cleanup, TESTFILE_LINK, &c_buf); + TST_EXP_PASS(fchownat(AT_FDCWD, TESTFILE_LINK, set_uid, set_gid, AT_SYMLINK_NOFOLLOW), + "fchownat(%d, %s, %d, %d, %d)", + AT_FDCWD, TESTFILE_LINK, set_uid, set_gid, AT_SYMLINK_NOFOLLOW); - SAFE_LSTAT(cleanup, TESTFILE_LINK, &l_buf); + SAFE_STAT(TESTFILE_LINK, &c_buf); + SAFE_LSTAT(TESTFILE_LINK, &l_buf); - if (c_buf.st_uid != set_uid && l_buf.st_uid == set_uid && - c_buf.st_gid != set_gid && l_buf.st_gid == set_gid) { - tst_resm(TPASS, "fchownat() test AT_SYMLINK_NOFOLLOW success"); - } else { - tst_resm(TFAIL, - "fchownat() test AT_SYMLINK_NOFOLLOW fail with uid=%d " - "link_uid=%d set_uid=%d | gid=%d link_gid=%d " - "set_gid=%d", c_buf.st_uid, l_buf.st_uid, set_uid, - c_buf.st_gid, l_buf.st_gid, set_gid); - } + TST_EXP_EXPR(c_buf.st_uid != set_uid && l_buf.st_uid == set_uid, + "fchownat() correctly operated on symlink user ID"); + TST_EXP_EXPR(c_buf.st_gid != set_gid && l_buf.st_gid == set_gid, + "fchownat() correctly operated on symlink group ID"); } -static void cleanup(void) -{ - tst_rmdir(); -} +static struct tst_test test = { + .needs_tmpdir = 1, + .needs_root = 1, + .test_all = run, + .setup = setup, +}; diff --git a/testcases/kernel/syscalls/fchownat/fchownat03.c b/testcases/kernel/syscalls/fchownat/fchownat03.c new file mode 100644 index 00000000..93ec5007 --- /dev/null +++ b/testcases/kernel/syscalls/fchownat/fchownat03.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Linux Test Project, 2007-2024 + */ + +/*\ + * Verify that fchownat(2) returns -1 and sets errno to: + * + * - EACCES if there is no permission to access to the file. + * - EBADF if the file descriptor of the specified file is not valid. + * - EFAULT if the filename points outside of accessable address space. + * - EINVAL if the flag is invalid. + * - ELOOP if too many symbolic links were encountered in resolving filename. + * - ENAMETOOLONG if the filename is too long. + * - ENOENT if the specified file does not exist. + * - ENOTDIR if the file descriptor is a file. + * - EPERM if the effective user id of process does not match the owner of + * the file and the process is not super user. + * - EROFS if the file is readonly. + */ + +#define _GNU_SOURCE +#include +#include "tst_test.h" + +#define TESTFILE "testfile" +#define TESTFILE_EACCESS_DIR "eaccess" +#define TESTFILE_EACCESS TESTFILE_EACCESS_DIR"/eaccess" +#define TESTFILE_ELOOP "testfile_eloop" +#define TESTFILE_ENOENT "/tmp/does/not/exist" +#define TESTFILE_EPERM "/dev/null" +#define TESTFILE_EROFS_MNT "ro_mntpoint" +#define TESTFILE_EROFS TESTFILE_EROFS_MNT"/file" + +static int file_fd = -1; +static int no_fd = -1; +static int dir_fd = -1; + +static char *file_eaccess; +static char *file_ebadf; +static char *file_efault; +static char *file_einval; +static char *file_eloop; +static char *file_enametoolong; +static char *file_enoent; +static char *file_enotdir; +static char *file_eperm; +static char *file_erofs; + +static struct tcase { + char **filename; + char *desc; + int *fd; + int flag; + int exp_errno; +} tcases[] = { + {&file_eaccess, TESTFILE_EACCESS, &dir_fd, 0, EACCES}, + {&file_ebadf, TESTFILE, &no_fd, 0, EBADF}, + {&file_efault, "Invalid address", &dir_fd, 0, EFAULT}, + {&file_einval, TESTFILE, &dir_fd, 9999, EINVAL}, + {&file_eloop, TESTFILE_ELOOP, &dir_fd, 0, ELOOP}, + {&file_enametoolong, "aaaa...", &dir_fd, 0, ENAMETOOLONG}, + {&file_enoent, TESTFILE_ENOENT, &dir_fd, 0, ENOENT}, + {&file_enotdir, TESTFILE, &file_fd, 0, ENOTDIR}, + {&file_eperm, TESTFILE_EPERM, &dir_fd, 0, EPERM}, + {&file_erofs, TESTFILE_EROFS, &dir_fd, 0, EROFS}, +}; + +static void fchownat_verify(unsigned int n) +{ + uid_t euid = geteuid(); + gid_t egid = getegid(); + + TST_EXP_FAIL(fchownat(*tcases[n].fd, *tcases[n].filename, euid, egid, tcases[n].flag), + tcases[n].exp_errno, + "fchownat(%d, %s, %d, %d, %d)", + *tcases[n].fd, tcases[n].desc, euid, egid, tcases[n].flag); +} + +static void setup(void) +{ + struct passwd *ltpuser; + + SAFE_TOUCH(TESTFILE, 0600, NULL); + dir_fd = SAFE_OPEN("./", O_DIRECTORY); + + /* EACCES setting */ + SAFE_SETEUID(0); + SAFE_MKDIR(TESTFILE_EACCESS_DIR, S_IRWXU); + SAFE_TOUCH(TESTFILE_EACCESS, 0666, NULL); + ltpuser = SAFE_GETPWNAM("nobody"); + SAFE_SETEUID(ltpuser->pw_uid); + + /* EFAULT setting */ + file_efault = tst_get_bad_addr(NULL); + + /* ENOTDIR setting */ + file_fd = SAFE_OPEN("file_fd", O_CREAT | O_RDWR, 0600); + + /* ELOOP setting */ + SAFE_SYMLINK(TESTFILE_ELOOP, "test_file_eloop2"); + SAFE_SYMLINK("test_file_eloop2", TESTFILE_ELOOP); + + /* ENAMETOOLONG setting */ + memset(file_enametoolong, 'a', PATH_MAX+1); + file_enametoolong[PATH_MAX+1] = 0; +} + +static void cleanup(void) +{ + if (dir_fd != -1) + SAFE_CLOSE(dir_fd); + if (file_fd != -1) + SAFE_CLOSE(file_fd); +} + +static struct tst_test test = { + .needs_tmpdir = 1, + .needs_root = 1, + .needs_rofs = 1, + .mntpoint = TESTFILE_EROFS_MNT, + .test = fchownat_verify, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .bufs = (struct tst_buffers []) { + {&file_eaccess, .str = TESTFILE_EACCESS}, + {&file_ebadf, .str = TESTFILE}, + {&file_einval, .str = TESTFILE}, + {&file_eloop, .str = TESTFILE_ELOOP}, + {&file_enametoolong, .size = PATH_MAX+2}, + {&file_enoent, .str = TESTFILE_ENOENT}, + {&file_enotdir, .str = TESTFILE}, + {&file_eperm, .str = TESTFILE_EPERM}, + {&file_erofs, .str = TESTFILE_EROFS}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/fcntl/.gitignore b/testcases/kernel/syscalls/fcntl/.gitignore index 10cb0995..e3486ee4 100755 --- a/testcases/kernel/syscalls/fcntl/.gitignore +++ b/testcases/kernel/syscalls/fcntl/.gitignore @@ -50,8 +50,6 @@ /fcntl26_64 /fcntl27 /fcntl27_64 -/fcntl28 -/fcntl28_64 /fcntl29 /fcntl29_64 /fcntl30 @@ -74,3 +72,5 @@ /fcntl38_64 /fcntl39 /fcntl39_64 +/fcntl40 +/fcntl40_64 diff --git a/testcases/kernel/syscalls/fcntl/Makefile b/testcases/kernel/syscalls/fcntl/Makefile index df663a50..aac77411 100755 --- a/testcases/kernel/syscalls/fcntl/Makefile +++ b/testcases/kernel/syscalls/fcntl/Makefile @@ -17,6 +17,6 @@ include $(abs_srcdir)/../utils/newer_64.mk %_64: CPPFLAGS += -D_FILE_OFFSET_BITS=64 -CPPFLAGS += -D_GNU_SOURCE +CPPFLAGS += -D_GNU_SOURCE -D_LARGEFILE64_SOURCE include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/fcntl/fcntl02.c b/testcases/kernel/syscalls/fcntl/fcntl02.c index 673c6919..278a8b9e 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl02.c +++ b/testcases/kernel/syscalls/fcntl/fcntl02.c @@ -36,11 +36,11 @@ static void verify_fcntl(unsigned int n) if (TST_RET < min_fd) { tst_res(TFAIL, "fcntl(%s, F_DUPFD, %i) returned %ld < %i", fname, min_fd, TST_RET, min_fd); + } else { + tst_res(TPASS, "fcntl(%s, F_DUPFD, %i) returned %ld", + fname, min_fd, TST_RET); } - tst_res(TPASS, "fcntl(%s, F_DUPFD, %i) returned %ld", - fname, min_fd, TST_RET); - SAFE_CLOSE(TST_RET); } diff --git a/testcases/kernel/syscalls/fcntl/fcntl05.c b/testcases/kernel/syscalls/fcntl/fcntl05.c index 7835d1e3..29cb20f4 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl05.c +++ b/testcases/kernel/syscalls/fcntl/fcntl05.c @@ -15,8 +15,6 @@ */ /*\ - * [Description] - * * Basic test for fcntl(2) using F_GETLK argument. * * If the lock could be placed, fcntl() does not actually place it, but diff --git a/testcases/kernel/syscalls/fcntl/fcntl07.c b/testcases/kernel/syscalls/fcntl/fcntl07.c index 9108b79c..10d1186f 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl07.c +++ b/testcases/kernel/syscalls/fcntl/fcntl07.c @@ -101,7 +101,7 @@ static void verify_cloexec(struct tcase *tc) sprintf(pidname, "%d", fd); - switch (pid = FORK_OR_VFORK()) { + switch (pid = tst_fork()) { case -1: tst_resm(TBROK | TERRNO, "fork() failed"); return; diff --git a/testcases/kernel/syscalls/fcntl/fcntl08.c b/testcases/kernel/syscalls/fcntl/fcntl08.c index 5241adfe..50619748 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl08.c +++ b/testcases/kernel/syscalls/fcntl/fcntl08.c @@ -1,100 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. - * AUTHOR : William Roske - * CO-PILOT : Dave Fenner - * - * 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/ - * + * Authors: William Roske, Dave Fenner + * Copyright (c) 2014 Fujitsu Ltd. + * Copyright (c) Linux Test Project, 2005-2015 + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -#include -#include -#include -#include -#include -#include -#include +/*\ + * Basic test for fcntl(2) using F_SETFL with flags O_NDELAY | O_APPEND | O_NONBLOCK. + */ -#include "test.h" -#include "safe_macros.h" - -static void setup(void); -static void cleanup(void); - -char *TCID = "fcntl08"; -int TST_TOTAL = 1; +#include "lapi/fcntl.h" +#include "tst_test.h" static int fd; -int main(int ac, char **av) +static void setup(void) { - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - TEST(fcntl(fd, F_SETFL, O_NDELAY | O_APPEND | O_NONBLOCK)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "fcntl failed"); - } else { - tst_resm(TPASS, "fcntl returned %ld", - TEST_RETURN); - } - - } - - cleanup(); - tst_exit(); + fd = SAFE_OPEN("testfile", O_RDWR | O_CREAT, 0700); } -void setup(void) +static void cleanup(void) { - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - fd = SAFE_OPEN(cleanup, "test_file", O_RDWR | O_CREAT, 0700); + if (fd > 0) + SAFE_CLOSE(fd); } -void cleanup(void) +static void run(void) { - if (close(fd) == -1) - tst_resm(TWARN | TERRNO, "close failed"); - - tst_rmdir(); + TST_EXP_PASS(fcntl(fd, F_SETFL, O_NDELAY | O_APPEND | O_NONBLOCK)); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/fcntl/fcntl11.c b/testcases/kernel/syscalls/fcntl/fcntl11.c index d042c6b9..4fd9fcca 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl11.c +++ b/testcases/kernel/syscalls/fcntl/fcntl11.c @@ -245,10 +245,6 @@ int main(int ac, char **av) int lc; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, "ddddd", &parent_pipe[0], - &parent_pipe[1], &child_pipe[0], &child_pipe[1], &fd); -#endif setup(); /* global setup */ @@ -257,17 +253,9 @@ int main(int ac, char **av) /* reset tst_count in case we are looping */ tst_count = 0; - if ((child_pid = FORK_OR_VFORK()) == 0) { /* parent */ -#ifdef UCLINUX - if (self_exec(av[0], "ddddd", parent_pipe[0], - parent_pipe[1], child_pipe[0], - child_pipe[1], fd) < 0) - tst_brkm(TBROK | TERRNO, cleanup, - "self_exec failed"); -#else + if ((child_pid = tst_fork()) == 0) /* parent */ do_child(); -#endif - } else if (child_pid == -1) + else if (child_pid == -1) tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); SAFE_CLOSE(cleanup, parent_pipe[0]); diff --git a/testcases/kernel/syscalls/fcntl/fcntl12.c b/testcases/kernel/syscalls/fcntl/fcntl12.c index ccd57da9..93b861e0 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl12.c +++ b/testcases/kernel/syscalls/fcntl/fcntl12.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the fcntl syscall. * * - EMFILE when cmd is F_DUPFD and the per-process limit on the number of open diff --git a/testcases/kernel/syscalls/fcntl/fcntl13.c b/testcases/kernel/syscalls/fcntl/fcntl13.c index 1bc414cc..6935e390 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl13.c +++ b/testcases/kernel/syscalls/fcntl/fcntl13.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the fcntl syscall. * * - EFAULT when lock is outside your accessible address space diff --git a/testcases/kernel/syscalls/fcntl/fcntl14.c b/testcases/kernel/syscalls/fcntl/fcntl14.c index ca68d0f9..c2f2e282 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl14.c +++ b/testcases/kernel/syscalls/fcntl/fcntl14.c @@ -1,1157 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com */ -/* - * NAME - * fcntl14.c - * - * DESCRIPTION - * File locking test cases for fcntl. In Linux, S_ENFMT is not implemented - * in the kernel. However all standard Unix kernels define S_ENFMT as - * S_ISGID. So this test defines S_ENFMT as S_ISGID. - * - * ALGORITHM - * Various test cases are used to lock a file opened without mandatory - * locking, with mandatory locking and mandatory locking with NOBLOCK - * - * USAGE - * fcntl14 - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * None +/*\ + * This test is checking fcntl() syscall locking mechanism between two + * processes. + * The test sets a random starting position on file using lseek(), it randomly + * generates fcntl() parameters for parent and child and it verifies that + * fcntl() will raise a blocking error on child when it's supposed to. */ -#define _GNU_SOURCE 1 + #include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include +#include "tst_test.h" -#define SKIP 0x0c00 -#if SKIP == F_RDLCK || SKIP== F_WRLCK -#error invalid value for SKIP, must be distinct from F_RDLCK and F_WRLCK -#endif #ifndef S_ENFMT -#define S_ENFMT S_ISGID +# define S_ENFMT S_ISGID #endif -/* NOBLOCK - immediate success */ -#define NOBLOCK 2 +#define CHECK_FAILURE(VAL_A, VAL_B) do { \ + TST_EXP_EQ_LI_SILENT(VAL_A, VAL_B); \ + if (!TST_PASS) \ + results->last_failed = 1; \ +} while (0) -/* WILLBLOCK - blocks, then succeeds (parent must unlock records) */ -#define WILLBLOCK 3 - -#define TIME_OUT 60 - -typedef struct { - short a_type; - short a_whence; - long a_start; - long a_len; - short b_type; /* SKIP means suppress fcntl call */ - short b_whence; - long b_start; - long b_len; - short c_type; - int c_whence; - long c_start; - long c_len; - short c_flag; -} testcase; - -static testcase testcases[] = { - /* Test cases: entire boundary */ - /* #1 Parent making a write lock on entire file */ - {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock on entire file */ - F_RDLCK, 0, 0L, 0L, WILLBLOCK}, - - /* #2 Parent making a write lock on entire file */ - {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock on entire file */ - F_WRLCK, 0, 0L, 0L, WILLBLOCK}, - - /* #3 Parent making a read lock on entire file */ - {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock on entire file */ - F_RDLCK, 0, 0L, 0L, NOBLOCK}, - - /* #4 Parent making a read lock on entire file */ - {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock on entire file */ - F_WRLCK, 0, 0L, 0L, WILLBLOCK}, - - /* Test case: start boundary */ - /* #5 Parent making a write lock on entire file */ - {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* - * Child attempting a read lock from beginning of - * file for 5 bytes - */ - F_RDLCK, 0, 0L, 5L, WILLBLOCK}, - - /* #6 Parent making a write lock on entire file */ - {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* - * Child attempting a write lock from beginning of - * file for 5 bytes - */ - F_WRLCK, 0, 0L, 5L, WILLBLOCK}, - - /* #7 Parent making a read lock on entire file */ - {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* - * Child attempting a read lock from beginning of - * file for 5 bytes - */ - F_RDLCK, 0, 0L, 5L, NOBLOCK}, - - /* #8 Parent making a read lock on entire file */ - {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* - * Child attempting a write lock from beginning of - * file for 5 bytes - */ - F_WRLCK, 0, 0L, 5L, WILLBLOCK}, - - /* Test cases: end boundary */ - /* #9 Parent making a write lock on entire file */ - {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 7 to end of file */ - F_RDLCK, 0, 7L, 0L, WILLBLOCK}, - - /* #10 Parent making a write lock on entire file */ - {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 7 to end of file */ - F_WRLCK, 0, 7L, 0L, WILLBLOCK}, - - /* #11 Parent making a read lock on entire file */ - {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 7 to end of file */ - F_RDLCK, 0, 7L, 0L, NOBLOCK}, - - /* #12 Parent making a read lock on entire file */ - {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 7 to end of file */ - F_WRLCK, 0, 7L, 0L, WILLBLOCK}, - - /* Test cases: entire boundary ( less than entire file) */ - /* - * #13 Parent making a write lock from beginning of - * file for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* - * Child attempting a read lock from beginning of - * file for 5 bytes - */ - F_RDLCK, 0, 0L, 5L, WILLBLOCK}, - - /* - * #14 Parent making a write lock from beginning of file - * for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* - * Child attempting a write lock from beginning of - * file for 5 bytes - */ - F_WRLCK, 0, 0L, 5L, WILLBLOCK}, - - /* - * #15 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* - * Child attempting a read lock from beginning of - * file for 5 bytes - */ - F_RDLCK, 0, 0L, 5L, NOBLOCK}, - - /* - * #16 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* - * Child attempting a write lock from beginning - * of file for 5 bytes - */ - F_WRLCK, 0, 0L, 5L, WILLBLOCK}, - - /* Test cases: inside boundary */ - /* - * #17 Parent making a write lock from beginning - * of file for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 2 to byte 4 */ - F_RDLCK, 0, 1L, 3L, WILLBLOCK}, - - /* - * #18 Parent making a write lock from beginning of - * file for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 2 to byte 4 */ - F_WRLCK, 0, 1L, 3L, WILLBLOCK}, - - /* - * #19 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 2 to byte 4 */ - F_RDLCK, 0, 1L, 3L, NOBLOCK}, - - /* - * #20 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 2 to byte 4 */ - F_WRLCK, 0, 1L, 3L, WILLBLOCK}, - - /* Test cases: cross boundary (inside to after) */ - /* - * #21 Parent making a write lock from beginning of - * file for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 3 to byte 7 */ - F_RDLCK, 0, 2L, 5L, WILLBLOCK}, - - /* - * #22 Parent making a write lock from beginning - * of file for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 3 to byte 7 */ - F_WRLCK, 0, 2L, 5L, WILLBLOCK}, - - /* - * #23 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 3 to byte 7 */ - F_RDLCK, 0, 2L, 5L, NOBLOCK}, - - /* - * #24 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 3 to byte 7 */ - F_WRLCK, 0, 2L, 5L, WILLBLOCK}, - - /* Test cases: outside boundary (after) */ - - /* - * #25 Parent making a write lock from beginning of - * file for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 7 to end of file */ - F_RDLCK, 0, 6L, 0L, NOBLOCK}, - - /* - * #26 Parent making a write lock from beginning of - * file for 5 bytes - */ - {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 7 to end of file */ - F_WRLCK, 0, 6L, 0L, NOBLOCK}, - - /* - * #27 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 7 to end of file */ - F_RDLCK, 0, 6L, 0L, NOBLOCK}, - - /* - * #28 Parent making a read lock from beginning of - * file for 5 bytes - */ - {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 7 to end of file */ - F_WRLCK, 0, 6L, 0L, NOBLOCK}, - - /* Test cases: outside boundary (before) */ - - /* #29 Parent making a write lock from byte 3 to byte 7 */ - {F_WRLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from beginning of file to byte 2 */ - F_RDLCK, 0, 0L, 2L, NOBLOCK}, - - /* #30 Parent making a write lock from byte 3 to byte 7 */ - {F_WRLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from beginning of file to byte 2 */ - F_WRLCK, 0, 0L, 2L, NOBLOCK}, - - /* #31 Parent making a write lock from byte 3 to byte 7 */ - {F_RDLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from beginning of file to byte 2 */ - F_RDLCK, 0, 0L, 2L, NOBLOCK}, - - /* #32 Parent making a write lock from byte 3 to byte 7 */ - {F_RDLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from beginning of file to byte 2 */ - F_WRLCK, 0, 0L, 2L, NOBLOCK}, - - /* Test cases: cross boundary (before to inside) */ - /* #33 Parent making a write lock from byte 5 to end of file */ - {F_WRLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 3 to byte 7 */ - F_RDLCK, 0, 2L, 5L, WILLBLOCK}, - - /* #34 Parent making a write lock from byte 5 to end of file */ - {F_WRLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 3 to byte 7 */ - F_WRLCK, 0, 2L, 5L, WILLBLOCK}, - - /* #35 Parent making a read lock from byte 5 to end of file */ - {F_RDLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a read lock from byte 3 to byte 7 */ - F_RDLCK, 0, 2L, 5L, NOBLOCK}, - - /* #36 Parent making a read lock from byte 5 to end of file */ - {F_RDLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting a write lock from byte 3 to byte 7 */ - F_WRLCK, 0, 2L, 5L, WILLBLOCK}, - - /* Start of negative L_start and L_len test cases */ - /* - * #37 Parent making write lock from byte 2 to byte 3 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 1 */ - F_WRLCK, 0, 1L, 1L, NOBLOCK}, - - /* - * #38 Parent making write lock from byte 2 to byte 3 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 4 */ - F_WRLCK, 0, 4L, 1L, NOBLOCK}, - - /* - * #39 Parent making write lock from byte 2 to byte 3 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 2 */ - F_WRLCK, 0, 2L, 1L, WILLBLOCK}, - - /* - * #40 Parent making write lock from byte 2 to byte 3 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 3 */ - F_WRLCK, 0, 3L, 1L, WILLBLOCK}, - - /* - * #41 Parent making write lock from byte 2 to byte 6 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 1 */ - F_WRLCK, 0, 1L, 1L, NOBLOCK}, - - /* - * #42 Parent making write lock from byte 2 to byte 6 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 7 */ - F_WRLCK, 0, 1L, 1L, NOBLOCK}, - - /* - * #43 Parent making write lock from byte 2 to byte 6 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 2 */ - F_WRLCK, 0, 2L, 1L, WILLBLOCK}, - - /* - * #44 Parent making write lock from byte 2 to byte 6 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 5 */ - F_WRLCK, 0, 5L, 1L, WILLBLOCK}, - - /* - * #45 Parent making write lock from byte 2 to byte 6 - * with L_start = -3 - */ - {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 6 */ - F_WRLCK, 0, 6L, 1L, WILLBLOCK}, - - /* - * #46 Parent making write lock from byte 2 to byte 3 with - * L_start = -2 and L_len = -2 - */ - {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 1 */ - F_WRLCK, 0, 1L, 1L, NOBLOCK}, - - /* - * #47 Parent making write lock from byte 2 to byte 3 with - * L_start = -2 and L_len = -2 - */ - {F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 4 */ - F_WRLCK, 0, 4L, 1L, NOBLOCK}, - - /* - * #48 Parent making write lock from byte 2 to byte 3 with - * L_start = -2 and L_len = -2 - */ - {F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 2 */ - F_WRLCK, 0, 2L, 1L, WILLBLOCK}, - - /* - * #49 Parent making write lock from byte 2 to byte 3 with - * L_start = -2 and L_len = -2 - */ - {F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 3 */ - F_WRLCK, 0, 3L, 1L, WILLBLOCK}, - - /* - * #50 Parent making write lock from byte 6 to byte 7 with - * L_start = 2 and L_len = -2 - */ - {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 5 */ - F_WRLCK, 0, 5L, 1L, NOBLOCK}, - - /* - * #51 Parent making write lock from byte 6 to byte 7 with - * L_start = 2 and L_len = -2 - */ - {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 8 */ - F_WRLCK, 0, 8L, 1L, NOBLOCK}, - - /* - * #52 Parent making write lock from byte 6 to byte 7 with - * L_start = 2 and L_len = -2 - */ - {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 6 */ - F_WRLCK, 0, 6L, 1L, WILLBLOCK}, - - /* - * #53 Parent making write lock from byte 6 to byte 7 with - * L_start = 2 and L_len = -2 - */ - {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 7 */ - F_WRLCK, 0, 7L, 1L, WILLBLOCK}, - - /* - * #54 Parent making write lock from byte 3 to byte 7 with - * L_start = 2 and L_len = -5 - */ - {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 2 */ - F_WRLCK, 0, 2L, 1L, NOBLOCK}, - - /* - * #55 Parent making write lock from byte 3 to byte 7 with - * L_start = 2 and L_len = -5 - */ - {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 8 */ - F_WRLCK, 0, 8L, 1L, NOBLOCK}, - - /* - * #56 Parent making write lock from byte 3 to byte 7 with - * L_start = 2 and L_len = -5 - */ - {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 3 */ - F_WRLCK, 0, 3L, 1L, WILLBLOCK}, - - /* - * #57 Parent making write lock from byte 3 to byte 7 with - * L_start = 2 and L_len = -5 - */ - {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 5 */ - F_WRLCK, 0, 5L, 1L, WILLBLOCK}, - - /* - * #58 Parent making write lock from byte 3 to byte 7 with - * L_start = 2 and L_len = -5 - */ - {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 7 */ - F_WRLCK, 0, 7L, 1L, WILLBLOCK}, - - /* Test case for block 4 */ - /* #59 Parent making write lock on entire file */ - {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, - /* Child attempting write lock on byte 15 to end of file */ - F_WRLCK, 0, 15L, 0L, WILLBLOCK}, +struct file_conf { + short type; + short whence; + long start; + long len; }; -static testcase *thiscase; -static struct flock flock; -static int parent, child, status, fail = 0; -static int got1 = 0; -static int fd; -static int test; -static char tmpname[40]; +struct testcase { + struct flock flock; + struct file_conf parent; /* parent parameters for fcntl() */ + struct file_conf child; /* child parameters for fcntl() */ + short blocking; /* blocking/non-blocking flag */ + long pos; /* starting file position */ +}; -#define FILEDATA "ten bytes!" +struct tc_results { + int num_pass; + int last_failed; +}; -void catch1(int sig); -void catch_alarm(int sig); +static const char filepath[] = "unlocked.txt"; +static const char filedata[] = "Here some bytes!"; +static char *str_op_nums; +static int op_nums = 5000; +static int file_mode = 0777; +static struct tc_results *results; -char *TCID = "fcntl14"; -int TST_TOTAL = 1; -int NO_NFS = 1; - -#ifdef UCLINUX -static char *argv0; -#endif - -void cleanup(void) +static void dochild(struct testcase *tc, const int fd, const pid_t parent_pid) { - tst_rmdir(); -} + struct flock flock = tc->flock; -void setup(void) -{ - struct sigaction act; + results->last_failed = 0; - tst_sig(FORK, DEF_HANDLER, cleanup); - signal(SIGHUP, SIG_IGN); - umask(0); - TEST_PAUSE; - tst_tmpdir(); - parent = getpid(); - - sprintf(tmpname, "fcntl2.%d", parent); - - /* setup signal handler for signal from child */ - memset(&act, 0, sizeof(act)); - act.sa_handler = catch1; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask, SIGUSR1); - if ((sigaction(SIGUSR1, &act, NULL)) < 0) { - tst_resm(TFAIL, "SIGUSR1 signal setup failed, errno = %d", - errno); - cleanup(); - } - - memset(&act, 0, sizeof(act)); - act.sa_handler = catch_alarm; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask, SIGALRM); - if ((sigaction(SIGALRM, &act, NULL)) < 0) { - tst_resm(TFAIL, "SIGALRM signal setup failed"); - cleanup(); - } -} - -void wake_parent(void) -{ - if ((kill(parent, SIGUSR1)) < 0) { - tst_resm(TFAIL, "Attempt to send signal to parent " "failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno); - fail = 1; - } -} - -void do_usleep_child(void) -{ - usleep(100000); /* XXX how long is long enough? */ - wake_parent(); - exit(0); -} - -void dochild(void) -{ - int rc; - pid_t pid; - - flock.l_type = thiscase->c_type; - flock.l_whence = thiscase->c_whence; - flock.l_start = thiscase->c_start; - flock.l_len = thiscase->c_len; - flock.l_pid = 0; - fail = 0; - - /* - * Check to see if child lock will succeed. If it will, FLOCK - * structure will return with l_type changed to F_UNLCK. If it will - * not, the parent pid will be returned in l_pid and the type of - * lock that will block it in l_type. - */ - if ((rc = fcntl(fd, F_GETLK, &flock)) < 0) { - tst_resm(TFAIL, "Attempt to check lock status failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno); - fail = 1; - } else { - - if ((thiscase->c_flag) == NOBLOCK) { - if (flock.l_type != F_UNLCK) { - tst_resm(TFAIL, - "Test case %d, GETLK: type = %d, " - "%d was expected", test + 1, - flock.l_type, F_UNLCK); - fail = 1; - } - - if (flock.l_whence != thiscase->c_whence) { - tst_resm(TFAIL, - "Test case %d, GETLK: whence = %d, " - "should have remained %d", test + 1, - flock.l_whence, thiscase->c_whence); - fail = 1; - } - - if (flock.l_start != thiscase->c_start) { - tst_resm(TFAIL, - "Test case %d, GETLK: start = %" PRId64 - ", " "should have remained %" PRId64, - test + 1, (int64_t) flock.l_start, - (int64_t) thiscase->c_start); - fail = 1; - } - - if (flock.l_len != thiscase->c_len) { - tst_resm(TFAIL, - "Test case %d, GETLK: len = %" PRId64 - ", " "should have remained %" PRId64, - test + 1, (int64_t) flock.l_len, - (int64_t) thiscase->c_len); - fail = 1; - } - - if (flock.l_pid != 0) { - tst_resm(TFAIL, - "Test case %d, GETLK: pid = %d, " - "should have remained 0", test + 1, - flock.l_pid); - fail = 1; - } - } else { - if (flock.l_pid != parent) { - tst_resm(TFAIL, - "Test case %d, GETLK: pid = %d, " - "should be parent's id of %d", - test + 1, flock.l_pid, parent); - fail = 1; - } - - if (flock.l_type != thiscase->a_type) { - tst_resm(TFAIL, - "Test case %d, GETLK: type = %d, " - "should be parent's first lock type of %d", - test + 1, flock.l_type, - thiscase->a_type); - fail = 1; - } - } - } - - /* - * now try to set the lock, nonblocking - * This will succeed for NOBLOCK, - * fail for WILLBLOCK - */ - flock.l_type = thiscase->c_type; - flock.l_whence = thiscase->c_whence; - flock.l_start = thiscase->c_start; - flock.l_len = thiscase->c_len; + flock.l_type = tc->child.type; + flock.l_whence = tc->child.whence; + flock.l_start = tc->child.start; + flock.l_len = tc->child.len; flock.l_pid = 0; - if ((rc = fcntl(fd, F_SETLK, &flock)) < 0) { - if ((thiscase->c_flag) == NOBLOCK) { - tst_resm(TFAIL, "Attempt to set child NONBLOCKING " - "lock failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", - test + 1, errno); - fail = 1; - } - } + SAFE_FCNTL(fd, F_GETLK, &flock); - if ((thiscase->c_flag) == WILLBLOCK) { - if (rc != -1 || (errno != EACCES && errno != EAGAIN)) { - tst_resm(TFAIL, - "SETLK: rc = %d, errno = %d, -1/EAGAIN or EACCES " - "was expected", rc, errno); - fail = 1; - } - if (rc == 0) { - /* accidentally got the lock */ - /* XXX how to clean up? */ - (void)fcntl(fd, F_UNLCK, &flock); - } - /* - * Lock should succeed after blocking and parent releases - * lock, tell the parent to release the locks. - * Do the lock in this process, send the signal in a child - * process, so that the SETLKW actually uses the blocking - * mechanism in the kernel. - * - * XXX inherent race: we want to wait until the - * F_SETLKW has started, but we don't have a way to - * check that reliably in the child. (We'd - * need some way to have fcntl() atomically unblock a - * signal and wait for the lock.) - */ - pid = FORK_OR_VFORK(); - switch (pid) { - case -1: - tst_resm(TFAIL, "Fork failed"); - fail = 1; - break; - case 0: -#ifdef UCLINUX - if (self_exec(argv0, "nd", 1, parent) < 0) { - tst_resm(TFAIL, "self_exec failed"); - break; - } -#else - do_usleep_child(); -#endif - break; + if (tc->blocking) { + tst_res(TDEBUG, "Child: expecting blocked file by parent"); - default: - if ((rc = fcntl(fd, F_SETLKW, &flock)) < 0) { - tst_resm(TFAIL, "Attempt to set child BLOCKING " - "lock failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", - test + 1, errno); - fail = 1; - } - waitpid(pid, &status, 0); - break; - } - } - if (fail) { - exit(1); + CHECK_FAILURE(flock.l_pid, parent_pid); + CHECK_FAILURE(flock.l_type, tc->parent.type); + + flock.l_type = tc->child.type; + flock.l_whence = tc->child.whence; + flock.l_start = tc->child.start; + flock.l_len = tc->child.len; + flock.l_pid = 0; + + TST_EXP_FAIL_SILENT(fcntl(fd, F_SETLK, &flock), EWOULDBLOCK); } else { + tst_res(TDEBUG, "Child: expecting no blocking errors"); + + CHECK_FAILURE(flock.l_type, F_UNLCK); + CHECK_FAILURE(flock.l_whence, tc->child.whence); + CHECK_FAILURE(flock.l_start, tc->child.start); + CHECK_FAILURE(flock.l_len, tc->child.len); + CHECK_FAILURE(flock.l_pid, 0); + + TST_EXP_PASS_SILENT(fcntl(fd, F_SETLK, &flock)); + } +} + +static void run_testcase(struct testcase *tc, const int file_mode) +{ + struct flock flock = tc->flock; + pid_t parent_pid; + pid_t child_pid; + int fd; + + tst_res(TDEBUG, "Parent: locking file"); + + /* open file and move cursor according with the test */ + fd = SAFE_OPEN(filepath, O_RDWR, file_mode); + SAFE_LSEEK(fd, tc->pos, 0); + + /* set the initial parent lock on the file */ + flock.l_type = tc->parent.type; + flock.l_whence = tc->parent.whence; + flock.l_start = tc->parent.start; + flock.l_len = tc->parent.len; + flock.l_pid = 0; + + SAFE_FCNTL(fd, F_SETLK, &flock); + + /* set the child lock on the file */ + parent_pid = getpid(); + child_pid = SAFE_FORK(); + + if (!child_pid) { + dochild(tc, fd, parent_pid); exit(0); } + + tst_reap_children(); + + flock.l_type = F_UNLCK; + flock.l_whence = 0; + flock.l_start = 0; + flock.l_len = 0; + flock.l_pid = 0; + + SAFE_CLOSE(fd); } -void run_test(int file_flag, int file_mode, int seek, int start, int end) +static void genconf(struct file_conf *conf, const int size, const long pos) { - fail = 0; - - for (test = start; test < end; test++) { - fd = SAFE_OPEN(cleanup, tmpname, file_flag, file_mode); - - if (write(fd, FILEDATA, 10) < 0) - tst_brkm(TBROK, cleanup, "write() failed"); - - if (seek) { - SAFE_LSEEK(cleanup, fd, seek, 0); - } - - thiscase = &testcases[test]; - flock.l_type = thiscase->a_type; - flock.l_whence = thiscase->a_whence; - flock.l_start = thiscase->a_start; - flock.l_len = thiscase->a_len; - - /* set the initial parent lock on the file */ - if ((fcntl(fd, F_SETLK, &flock)) < 0) { - tst_resm(TFAIL, "First parent lock failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", - test + 1, errno); - fail = 1; - } - - if ((thiscase->b_type) != SKIP) { - flock.l_type = thiscase->b_type; - flock.l_whence = thiscase->b_whence; - flock.l_start = thiscase->b_start; - flock.l_len = thiscase->b_len; - - /* set the second parent lock */ - if ((fcntl(fd, F_SETLK, &flock)) < 0) { - tst_resm(TFAIL, "Second parent lock failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", - test + 1, errno); - fail = 1; - } - } - if ((thiscase->c_type) == SKIP) { - close(fd); - tst_resm(TINFO, "skipping test %d", test + 1); - continue; - } - - /* Mask SIG_USR1 before forking child, to avoid race */ - (void)sighold(SIGUSR1); - - /* flush the stdout to avoid garbled output */ - fflush(stdout); - - if ((child = FORK_OR_VFORK()) == 0) { -#ifdef UCLINUX - if (self_exec(argv0, "nddddddddd", 2, thiscase->c_type, - thiscase->c_whence, thiscase->c_start, - thiscase->c_len, thiscase->c_flag, - thiscase->a_type, fd, test, parent) < 0) { - tst_resm(TFAIL, "self_exec failed"); - cleanup(); - } -#else - dochild(); -#endif - } - if (child < 0) - tst_brkm(TBROK|TERRNO, cleanup, "Fork failed"); - - if ((thiscase->c_flag) == WILLBLOCK) { - /* - * Wait for a signal from the child then remove - * blocking lock. Set a 60 second alarm to break the - * pause just in case the child never signals us. - */ - alarm(TIME_OUT); - sigpause(SIGUSR1); - - /* turn off the alarm timer */ - alarm((unsigned)0); - if (got1 != 1) - tst_resm(TINFO, "Pause terminated without " - "signal SIGUSR1 from child"); - got1 = 0; - - /* - * setup lock structure for parent to delete - * blocking lock then wait for child to exit - */ - flock.l_type = F_UNLCK; - flock.l_whence = 0; - flock.l_start = 0L; - flock.l_len = 0L; - if ((fcntl(fd, F_SETLK, &flock)) < 0) { - tst_resm(TFAIL, "Attempt to release parent " - "lock failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", - test + 1, errno); - fail = 1; - } - } - /* - * set a 60 second alarm to break the wait just in case the - * child doesn't terminate on its own accord - */ - alarm(TIME_OUT); - - /* wait for the child to terminate and close the file */ - waitpid(child, &status, 0); - /* turn off the alarm clock */ - alarm((unsigned)0); - if (status != 0) { - tst_resm(TFAIL, "tchild returned status 0x%x", status); - fail = 1; - } - close(fd); - if (fail) - tst_resm(TFAIL, "testcase:%d FAILED", test + 1); - else - tst_resm(TPASS, "testcase:%d PASSED", test + 1); - } - unlink(tmpname); + conf->type = rand() % 2 ? F_RDLCK : F_WRLCK; + conf->whence = SEEK_CUR; + conf->start = rand() % (size - 1); + conf->len = rand() % (size - conf->start - 1) + 1; + conf->start -= pos; } -void catch_alarm(int sig) +static short fcntl_overlap( + struct file_conf *parent, + struct file_conf *child) { - /* - * Timer has runout and child has not signaled, need - * to kill off the child as it appears it will not - * on its own accord. Check that it is still around - * as it may have terminated abnormally while parent - * was waiting for SIGUSR1 signal from it. - */ - if (kill(child, 0) == 0) { - kill(child, SIGKILL); - perror("The child didnot terminate on its own accord"); - } -} + short overlap; -void catch1(int sig) -{ - struct sigaction act; - - /* - * Set flag to let parent know that child is ready to have lock - * removed - */ - memset(&act, 0, sizeof(act)); - act.sa_handler = catch1; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask, SIGUSR1); - sigaction(SIGUSR1, &act, NULL); - got1++; -} - -static void testcheck_end(int check_fail, char *msg) -{ - if (check_fail) - tst_resm(TFAIL, "%s FAILED", msg); + if (child->start < parent->start) + overlap = parent->start < (child->start + child->len); else - tst_resm(TPASS, "%s PASSED", msg); + overlap = child->start < (parent->start + parent->len); + + if (overlap) + tst_res(TDEBUG, "child/parent fcntl() configurations overlap"); + + return overlap; } -int main(int ac, char **av) +static void gentestcase(struct testcase *tc) { - int lc; + struct file_conf *parent = &tc->parent; + struct file_conf *child = &tc->child; + int size; - tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - argv0 = av[0]; + memset(&tc->flock, 0, sizeof(struct flock)); - maybe_run_child(&do_usleep_child, "nd", 1, &parent); - thiscase = malloc(sizeof(testcase)); + size = strlen(filedata); + tc->pos = rand() % size; - maybe_run_child(&dochild, "nddddddddd", 2, &thiscase->c_type, - &thiscase->c_whence, &thiscase->c_start, - &thiscase->c_len, &thiscase->c_flag, &thiscase->a_type, - &fd, &test, &parent); -#endif + genconf(parent, size, tc->pos); + genconf(child, size, tc->pos); - setup(); + tc->blocking = fcntl_overlap(parent, child); - if (tst_fs_type(cleanup, ".") == TST_NFS_MAGIC) - NO_NFS = 0; - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - -/* //block1: */ - tst_resm(TINFO, "Enter block 1: without mandatory locking"); - fail = 0; - /* - * try various file locks on an ordinary file without - * mandatory locking - */ - (void)run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 0, 36); - testcheck_end(fail, "Block 1, test 1"); - - /* Now try with negative values for L_start and L_len */ - (void)run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 5, 36, 45); - testcheck_end(fail, "Block 1, test 2"); - - tst_resm(TINFO, "Exit block 1"); - -/* //block2: */ - /* - * Skip block2 if test on NFS, since NFS does not support - * mandatory locking - */ - tst_resm(TINFO, "Enter block 2: with mandatory locking"); - if (NO_NFS) { - fail = 0; - /* - * Try various locks on a file with mandatory - * record locking this should behave the same - * as an ordinary file - */ - (void)run_test(O_CREAT | O_RDWR | O_TRUNC, - S_ENFMT | S_IRUSR | S_IWUSR, 0, 0, 36); - testcheck_end(fail, "Block 2, test 1"); - - /* Now try negative values for L_start and L_len */ - (void)run_test(O_CREAT | O_RDWR | O_TRUNC, - S_ENFMT | S_IRUSR | S_IWUSR, 5, 36, 45); - testcheck_end(fail, "Block 2, test 2"); - } else { - tst_resm(TCONF, "Skip block 2 as NFS does not" - " support mandatory locking"); - } - - tst_resm(TINFO, "Exit block 2"); - -/* //block3: */ - tst_resm(TINFO, "Enter block 3"); - fail = 0; - /* - * Check that proper error status is returned when invalid - * argument used for WHENCE (negative value) - */ - - fd = SAFE_OPEN(cleanup, tmpname, O_CREAT | O_RDWR | O_TRUNC, - 0777); - - if (write(fd, FILEDATA, 10) < 0) - tst_brkm(TBROK, cleanup, "write failed"); - - flock.l_type = F_WRLCK; - flock.l_whence = -1; - flock.l_start = 0L; - flock.l_len = 0L; - - if ((fcntl(fd, F_SETLK, &flock)) < 0) { - if (errno != EINVAL) { - tst_resm(TFAIL, "Expected %d got %d", - EINVAL, errno); - fail = 1; - } - } else { - tst_resm(TFAIL, "Lock succeeded when it should have " - "failed"); - fail = 1; - } - - close(fd); - unlink(tmpname); - - testcheck_end(fail, "Test with negative whence locking"); - tst_resm(TINFO, "Exit block 3"); - -/* //block4: */ - tst_resm(TINFO, "Enter block 4"); - fail = 0; - /* - * Check that a lock on end of file is still valid when - * additional data is appended to end of file and a new - * process attempts to lock new data - */ - fd = SAFE_OPEN(cleanup, tmpname, O_CREAT | O_RDWR | O_TRUNC, - 0777); - - if (write(fd, FILEDATA, 10) < 0) - tst_brkm(TBROK, cleanup, "write failed"); - - thiscase = &testcases[58]; - flock.l_type = thiscase->a_type; - flock.l_whence = thiscase->a_whence; - flock.l_start = thiscase->a_start; - flock.l_len = thiscase->a_len; - - /* Set the initial parent lock on the file */ - if ((fcntl(fd, F_SETLK, &flock)) < 0) { - tst_resm(TFAIL, "First parent lock failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", 58, errno); - fail = 1; - } - - /* Write some additional data to end of file */ - if (write(fd, FILEDATA, 10) < 0) - tst_brkm(TBROK, cleanup, "write failed"); - - /* Mask signal to avoid race */ - if (sighold(SIGUSR1) < 0) - tst_brkm(TBROK, cleanup, "sighold failed"); - - if ((child = FORK_OR_VFORK()) == 0) { -#ifdef UCLINUX - if (self_exec(argv0, "nddddddddd", 2, thiscase->c_type, - thiscase->c_whence, thiscase->c_start, - thiscase->c_len, thiscase->c_flag, - thiscase->a_type, fd, test, parent) < 0) { - tst_resm(TFAIL, "self_exec failed"); - cleanup(); - } -#else - dochild(); -#endif - } - if (child < 0) - tst_brkm(TBROK|TERRNO, cleanup, "Fork failed"); - - /* - * Wait for a signal from the child then remove blocking lock. - * Set a 60 sec alarm to break the pause just in case the - * child doesn't terminate on its own accord - */ - (void)alarm(TIME_OUT); - - (void)sigpause(SIGUSR1); - - /* turn off the alarm timer */ - (void)alarm((unsigned)0); - if (got1 != 1) { - tst_resm(TINFO, "Pause terminated without signal " - "SIGUSR1 from child"); - } - got1 = 0; - - /* - * Set up lock structure for parent to delete - * blocking lock then wait for child to exit - */ - flock.l_type = F_UNLCK; - flock.l_whence = 0; - flock.l_start = 0L; - flock.l_len = 0L; - if ((fcntl(fd, F_SETLK, &flock)) < 0) { - tst_resm(TFAIL, "Attempt to release parent lock " - "failed"); - tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, - errno); - fail = 1; - } - - /* - * set a 60 sec alarm to break the wait just in case the - * child doesn't terminate on its own accord - */ - (void)alarm(TIME_OUT); - - waitpid(child, &status, 0); - if (WEXITSTATUS(status) != 0) { - fail = 1; - tst_resm(TFAIL, "child returned bad exit status"); - } - - /* turn off the alarm clock */ - (void)alarm((unsigned)0); - if (status != 0) { - tst_resm(TFAIL, "child returned status 0x%x", status); - fail = 1; - } - close(fd); - unlink(tmpname); - - testcheck_end(fail, "Test of locks on file"); - tst_resm(TINFO, "Exit block 4"); - } - cleanup(); - tst_exit(); + if (parent->type == F_RDLCK && child->type == F_RDLCK) + tc->blocking = 0; } + +static void print_testcase(struct testcase *tc) +{ + tst_res(TDEBUG, "Starting read/write position: %ld", tc->pos); + + tst_res(TDEBUG, + "Parent: type=%s whence=%s start=%ld len=%ld", + tc->parent.type == F_RDLCK ? "F_RDLCK" : "F_WRLCK", + tc->parent.whence == SEEK_SET ? "SEEK_SET" : "SEEK_CUR", + tc->parent.start, + tc->parent.len); + + tst_res(TDEBUG, + "Child: type=%s whence=%s start=%ld len=%ld", + tc->child.type == F_RDLCK ? "F_RDLCK" : "F_WRLCK", + tc->child.whence == SEEK_SET ? "SEEK_SET" : "SEEK_CUR", + tc->child.start, + tc->child.len); + + tst_res(TDEBUG, "Expencted %s test", + tc->blocking ? "blocking" : "non-blocking"); +} + +static void run(void) +{ + struct testcase tc; + + results->num_pass = 0; + + for (int i = 0; i < op_nums; i++) { + gentestcase(&tc); + print_testcase(&tc); + + tst_res(TDEBUG, "Running test #%u", i); + run_testcase(&tc, file_mode); + + if (results->last_failed) + return; + + results->num_pass++; + } + + if (results->num_pass == op_nums) + tst_res(TPASS, "All %d test file operations passed", op_nums); +} + +static void setup(void) +{ + int fd; + + if (tst_parse_int(str_op_nums, &op_nums, 1, INT_MAX)) + tst_brk(TBROK, "Invalid number of operations '%s'", str_op_nums); + + if (tst_variant == 1) { + tst_res(TINFO, "Requested mandatory locking"); + + file_mode = S_ENFMT | 0600; + } + + fd = SAFE_OPEN(filepath, O_CREAT | O_RDWR | O_TRUNC, 0777); + SAFE_WRITE(SAFE_WRITE_ALL, fd, filedata, strlen(filedata)); + SAFE_CLOSE(fd); + + srand(time(0)); + + results = SAFE_MMAP( + NULL, + sizeof(struct tc_results), + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, + -1, 0); +} + +static void cleanup(void) +{ + if (results) + SAFE_MUNMAP(results, sizeof(struct tc_results)); +} + +static struct tst_test test = { + .timeout = 8, + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .test_variants = 2, + .forks_child = 1, + .needs_tmpdir = 1, + .options = (struct tst_option[]) { + { "n:", &str_op_nums, "Total # operations to do (default 5000)" }, + {}, + }, + .skip_filesystems = (const char *const []) { + "nfs", + NULL + }, +}; diff --git a/testcases/kernel/syscalls/fcntl/fcntl15.c b/testcases/kernel/syscalls/fcntl/fcntl15.c index 8c17144f..79184e0c 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl15.c +++ b/testcases/kernel/syscalls/fcntl/fcntl15.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Check that file locks are removed when a file descriptor is closed, three * different tests are implemented. * diff --git a/testcases/kernel/syscalls/fcntl/fcntl16.c b/testcases/kernel/syscalls/fcntl/fcntl16.c index a77a8129..4ae9e6e7 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl16.c +++ b/testcases/kernel/syscalls/fcntl/fcntl16.c @@ -279,10 +279,6 @@ extern void catch_int(int sig); /* signal catching subroutine */ char *TCID = "fcntl16"; int TST_TOTAL = 1; -#ifdef UCLINUX -static char *argv0; -#endif - /* * cleanup - performs all the ONE TIME cleanup for this test at completion or * premature exit @@ -339,15 +335,6 @@ void dochild(int kid) exit(0); } /* end of child process */ -#ifdef UCLINUX -static int kid_uc; - -void dochild_uc(void) -{ - dochild(kid_uc); -} -#endif - void catch_alarm(int sig) { alarm_flag = 1; @@ -497,17 +484,8 @@ int run_test(int file_flag, int file_mode, int start, int end) /* spawn child processes */ for (i = 0; i < 2; i++) { if (thislock->l_type != IGNORED) { - if ((child = FORK_OR_VFORK()) == 0) { -#ifdef UCLINUX - if (self_exec(argv0, "ddddd", i, parent, - test, thislock, fd) < 0) { - perror("self_exec failed"); - return 1; - } -#else + if ((child = tst_fork()) == 0) dochild(i); -#endif - } if (child < 0) { perror("Fork failed"); return 1; @@ -654,11 +632,6 @@ int main(int ac, char **av) int lc; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(dochild_uc, "ddddd", &kid_uc, &parent, &test, - &thislock, &fd); - argv0 = av[0]; -#endif setup(); /* global setup */ diff --git a/testcases/kernel/syscalls/fcntl/fcntl17.c b/testcases/kernel/syscalls/fcntl/fcntl17.c index e055f1a1..5a52f0af 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl17.c +++ b/testcases/kernel/syscalls/fcntl/fcntl17.c @@ -438,23 +438,6 @@ int main(int ac, char **av) int fail = 0; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child1, "nddddddddd", 1, &file_fd, - &parent_pipe[0], &parent_pipe[1], - &child_pipe1[0], &child_pipe1[1], - &child_pipe2[0], &child_pipe2[1], - &child_pipe3[0], &child_pipe3[1]); - maybe_run_child(&do_child2, "nddddddddd", 2, &file_fd, - &parent_pipe[0], &parent_pipe[1], - &child_pipe1[0], &child_pipe1[1], - &child_pipe2[0], &child_pipe2[1], - &child_pipe3[0], &child_pipe3[1]); - maybe_run_child(&do_child3, "nddddddddd", 3, &file_fd, - &parent_pipe[0], &parent_pipe[1], - &child_pipe1[0], &child_pipe1[1], - &child_pipe2[0], &child_pipe2[1], - &child_pipe3[0], &child_pipe3[1]); -#endif if (setup()) { /* global testup */ tst_resm(TINFO, "setup failed"); @@ -467,56 +450,22 @@ int main(int ac, char **av) tst_count = 0; tst_resm(TINFO, "Enter preparation phase"); - if ((child_pid1 = FORK_OR_VFORK()) == 0) { /* first child */ -#ifdef UCLINUX - if (self_exec(av[0], "nddddddddd", 1, file_fd, - parent_pipe[0], parent_pipe[1], - child_pipe1[0], child_pipe1[1], - child_pipe2[0], child_pipe2[1], - child_pipe3[0], child_pipe3[1]) < 0) { - perror("self_exec failed, child 1"); - cleanup(); - } -#else + if ((child_pid1 = tst_fork()) == 0) /* first child */ do_child1(); -#endif - } else if (child_pid1 < 0) + else if (child_pid1 < 0) tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 1"); /* parent */ - if ((child_pid2 = fork()) == 0) { /* second child */ -#ifdef UCLINUX - if (self_exec(av[0], "nddddddddd", 2, file_fd, - parent_pipe[0], parent_pipe[1], - child_pipe1[0], child_pipe1[1], - child_pipe2[0], child_pipe2[1], - child_pipe3[0], child_pipe3[1]) < 0) { - perror("self_exec failed, child 2"); - cleanup(); - } -#else + if ((child_pid2 = fork()) == 0) /* second child */ do_child2(); -#endif - } else if (child_pid2 < 0) { + else if (child_pid2 < 0) tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 2"); - } /* parent */ if ((child_pid3 = fork()) == 0) { /* third child */ -#ifdef UCLINUX - if (self_exec(av[0], "nddddddddd", 3, file_fd, - parent_pipe[0], parent_pipe[1], - child_pipe1[0], child_pipe1[1], - child_pipe2[0], child_pipe2[1], - child_pipe3[0], child_pipe3[1]) < 0) { - perror("self_exec failed, child 3"); - cleanup(); - } -#else do_child3(); -#endif do_child3(); } else if (child_pid3 < 0) { tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 3"); diff --git a/testcases/kernel/syscalls/fcntl/fcntl18.c b/testcases/kernel/syscalls/fcntl/fcntl18.c index 1105dd39..7a26f65a 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl18.c +++ b/testcases/kernel/syscalls/fcntl/fcntl18.c @@ -70,8 +70,6 @@ int main(int ac, char **av) setup(); /* global setup */ /* //block1: */ -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ tst_resm(TINFO, "Enter block 1"); fail = 0; if ((fd = open("temp.dat", O_CREAT | O_RDWR, 0777)) < 0) { //mode must be specified when O_CREATE is in the flag @@ -93,13 +91,8 @@ int main(int ac, char **av) tst_resm(TINFO, "Block 1 PASSED"); } tst_resm(TINFO, "Exit block 1"); -#else - tst_resm(TINFO, "Skip block 1 on uClinux"); -#endif /* //block2: */ -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ tst_resm(TINFO, "Enter block 2"); fail = 0; /* Error condition if address is bad */ @@ -116,14 +109,11 @@ int main(int ac, char **av) tst_resm(TINFO, "Block 2 PASSED"); } tst_resm(TINFO, "Exit block 2"); -#else - tst_resm(TINFO, "Skip block 2 on uClinux"); -#endif /* //block3: */ tst_resm(TINFO, "Enter block 3"); fail = 0; - if ((pid = FORK_OR_VFORK()) == 0) { /* child */ + if ((pid = tst_fork()) == 0) { /* child */ fail = 0; pass = getpwnam("nobody"); retval = setreuid(-1, pass->pw_uid); diff --git a/testcases/kernel/syscalls/fcntl/fcntl19.c b/testcases/kernel/syscalls/fcntl/fcntl19.c index f929aff9..a58e921c 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl19.c +++ b/testcases/kernel/syscalls/fcntl/fcntl19.c @@ -284,10 +284,6 @@ int main(int ac, char **av) int lc; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, "ddddd", &parent_pipe[0], &parent_pipe[1], - &child_pipe[0], &child_pipe[1], &fd); -#endif setup(); /* global setup */ @@ -296,17 +292,8 @@ int main(int ac, char **av) /* reset tst_count in case we are looping */ tst_count = 0; - if ((child_pid = FORK_OR_VFORK()) == 0) { /* child */ -#ifdef UCLINUX - if (self_exec - (av[0], "ddddd", parent_pipe[0], parent_pipe[1], - child_pipe[0], child_pipe[1], fd) < 0) { - tst_resm(TFAIL, "self_exec failed"); - cleanup(); - } -#else + if ((child_pid = tst_fork()) == 0) { /* child */ do_child(); -#endif } else if (child_pid < 0) { tst_resm(TFAIL, "Fork failed"); cleanup(); diff --git a/testcases/kernel/syscalls/fcntl/fcntl20.c b/testcases/kernel/syscalls/fcntl/fcntl20.c index 4aa77345..f271eeb2 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl20.c +++ b/testcases/kernel/syscalls/fcntl/fcntl20.c @@ -283,10 +283,6 @@ int main(int ac, char **av) int lc; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, "ddddd", &parent_pipe[0], &parent_pipe[1], - &child_pipe[0], &child_pipe[1], &fd); -#endif setup(); /* global setup */ @@ -295,18 +291,8 @@ int main(int ac, char **av) /* reset tst_count in case we are looping */ tst_count = 0; - if ((child_pid = FORK_OR_VFORK()) == 0) { /* child */ -#ifdef UCLINUX - if (self_exec - (av[0], "ddddd", parent_pipe[0], parent_pipe[1], - child_pipe[0], child_pipe[1], fd) < 0) { - tst_resm(TFAIL, "self_exec failed"); - cleanup(); - } -#else + if ((child_pid = tst_fork()) == 0) /* child */ do_child(); -#endif - } if (child_pid < 0) { tst_resm(TFAIL, "Fork failed"); diff --git a/testcases/kernel/syscalls/fcntl/fcntl21.c b/testcases/kernel/syscalls/fcntl/fcntl21.c index 824b8c05..a262ae8d 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl21.c +++ b/testcases/kernel/syscalls/fcntl/fcntl21.c @@ -291,11 +291,6 @@ int main(int ac, char **av) int lc; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, "ddddd", &parent_pipe[0], &parent_pipe[1], - &child_pipe[0], &child_pipe[1], &fd); -#endif - setup(); /* global setup */ /* Check for looping state if -i option is given */ @@ -303,18 +298,9 @@ int main(int ac, char **av) /* reset tst_count in case we are looping */ tst_count = 0; - if ((child_pid = FORK_OR_VFORK()) == 0) { -#ifdef UCLINUX - if (self_exec - (av[0], "ddddd", parent_pipe[0], parent_pipe[1], - child_pipe[0], child_pipe[1], fd) < 0) { - tst_resm(TFAIL, "self_exec failed"); - cleanup(); - } -#else + if ((child_pid = tst_fork()) == 0) do_child(); -#endif - } + if (child_pid < 0) { tst_resm(TFAIL, "Fork failed"); cleanup(); diff --git a/testcases/kernel/syscalls/fcntl/fcntl22.c b/testcases/kernel/syscalls/fcntl/fcntl22.c index 2e94a125..50793438 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl22.c +++ b/testcases/kernel/syscalls/fcntl/fcntl22.c @@ -60,7 +60,7 @@ int main(int ac, char **av) for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; - child_pid = FORK_OR_VFORK(); + child_pid = tst_fork(); switch (child_pid) { case 0: TEST(fcntl(file, F_SETLK, &fl)); diff --git a/testcases/kernel/syscalls/fcntl/fcntl27.c b/testcases/kernel/syscalls/fcntl/fcntl27.c index eeb5d63c..07a63ebc 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl27.c +++ b/testcases/kernel/syscalls/fcntl/fcntl27.c @@ -1,192 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (C) Bull S.A. 2005 - * Copyright (c) International Business Machines Corp., 2004 - * - * 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 + * Copyright (c) International Business Machines Corp., 2004 + * Copyright (C) Bull S.A.S 2005-2006 + * Author: Jacky Malcles + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -/********************************************************** - * - * TEST IDENTIFIER : fcntl27 - * - * EXECUTED BY : anyone - * - * TEST TITLE : Basic test for fcntl(2) using F_SETLEASE & F_RDLCK argument. - * - * TEST CASE TOTAL : 1 - * - * WALL CLOCK TIME : 1 - * - * CPU TYPES : ALL - * - * AUTHOR : Jacky Malcles - * - * TEST CASES - * - * 1.) fcntl(2) returns...(See Description) - * - * INPUT SPECIFICATIONS - * The standard options for system call tests are accepted. - * (See the parse_opts(3) man page). - * - * OUTPUT SPECIFICATIONS - * - * DURATION - * Terminates - with frequency and infinite modes. - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * RESOURCES - * None - * - * ENVIRONMENTAL NEEDS - * No run-time environmental needs. - * - * SPECIAL PROCEDURAL REQUIREMENTS - * None - * - * INTERCASE DEPENDENCIES - * None - * - * DETAILED DESCRIPTION - * This is a Phase I test for the fcntl(2) system call. It is intended - * to provide a limited exposure of the system call, for now. It - * should/will be extended when full functional tests are written for - * fcntl(2). - * - * Setup: - * Setup signal handling. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, Issue a PASS message. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * - * - *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/ +/*\ + * Basic test for fcntl(2) using F_SETLEASE and F_RDLCK argument, + * testing O_RDWR and O_WRONLY. + */ -#include -#include -#include -#include -#include -#include -#include -#include "test.h" +#include "lapi/fcntl.h" +#include "tst_test.h" -void setup(); -void cleanup(); +#define TC(x, y) .oflags = x, .mode = y, .desc = #x " with mode " #y, -char *TCID = "fcntl27"; -int TST_TOTAL = 1; - -char fname[255]; -int fd; - -int main(int ac, char **av) +static struct test_case { - int lc, expected_result = -1; - /*************************************************************** - * parse standard options - ***************************************************************/ - tst_parse_opts(ac, av, NULL, NULL); + int oflags; + mode_t mode; + char *desc; +} tcases[] = { + { TC(O_RDWR | O_CREAT, 0777) }, + { TC(O_WRONLY | O_CREAT, 0222) }, +}; - /*************************************************************** - * perform global setup for test - ***************************************************************/ - setup(); +static void verify_fcntl(unsigned int nr) +{ + struct test_case *tc = &tcases[nr]; - expected_result = -1; + tst_res(TINFO, "Testing %s", tc->desc); - /*************************************************************** - * check looping state if -c option given - ***************************************************************/ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - -#ifdef F_SETLEASE - /* - * Call fcntl(2) with F_SETLEASE & F_RDLCK argument on fname - */ - TEST(fcntl(fd, F_SETLEASE, F_RDLCK)); - - /* check return code */ - if (TEST_RETURN == expected_result) { - tst_resm(TPASS, - "fcntl(fd, F_SETLEASE, F_RDLCK) succeeded"); - } else { - tst_resm(TFAIL, "fcntl(%s, F_SETLEASE, F_RDLCK)" - " failed with errno %d : %s", fname, - TEST_ERRNO, strerror(TEST_ERRNO)); - } -#else - tst_resm(TINFO, "F_SETLEASE not defined, skipping test"); -#endif - } - - /*************************************************************** - * cleanup and exit - ***************************************************************/ - cleanup(); - tst_exit(); + int fd = SAFE_OPEN("testfile", tc->oflags, tc->mode); + TST_EXP_FAIL(fcntl(fd, F_SETLEASE, F_RDLCK), EAGAIN); + SAFE_CLOSE(fd); } -/*************************************************************** - * setup() - performs all ONE TIME setup for this test. - ***************************************************************/ -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - sprintf(fname, "tfile_%d", getpid()); - if ((fd = open(fname, O_RDWR | O_CREAT, 0777)) == -1) { - tst_brkm(TBROK, cleanup, - "open(%s, O_RDWR|O_CREAT,0777) Failed, errno=%d : %s", - fname, errno, strerror(errno)); - } -} - -/*************************************************************** - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - ***************************************************************/ -void cleanup(void) -{ - - /* close the file we've had open */ - if (close(fd) == -1) { - tst_resm(TWARN, "close(%s) Failed, errno=%d : %s", fname, errno, - strerror(errno)); - } - - tst_rmdir(); - -} +static struct tst_test test = { + .test = verify_fcntl, + .tcnt = ARRAY_SIZE(tcases), + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/fcntl/fcntl28.c b/testcases/kernel/syscalls/fcntl/fcntl28.c deleted file mode 100755 index 4da04c48..00000000 --- a/testcases/kernel/syscalls/fcntl/fcntl28.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) Bull S.A.S 2006 - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/********************************************************** - * - * TEST IDENTIFIER : fcntl28 - * - * EXECUTED BY : anyone - * - * TEST TITLE : Basic test for fcntl(2) using F_SETLEASE & F_RDLCK argument. - * - * TEST CASE TOTAL : 1 - * - * WALL CLOCK TIME : 1 - * - * CPU TYPES : ALL - * - * AUTHOR : Jacky Malcles - * - * TEST CASES - * - * 1.) fcntl(2) returns...(See Description) - * - * INPUT SPECIFICATIONS - * The standard options for system call tests are accepted. - * (See the parse_opts(3) man page). - * - * OUTPUT SPECIFICATIONS - * - * DURATION - * Terminates - with frequency and infinite modes. - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * RESOURCES - * None - * - * ENVIRONMENTAL NEEDS - * No run-time environmental needs. - * - * SPECIAL PROCEDURAL REQUIREMENTS - * None - * - * INTERCASE DEPENDENCIES - * None - * - * DETAILED DESCRIPTION - * This is a Phase I test for the fcntl(2) system call. It is intended - * to provide a limited exposure of the system call, for now. It - * should/will be extended when full functional tests are written for - * fcntl(2). - * - * Setup: - * Setup signal handling. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, Issue a PASS message. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * - * - *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/ - -#include -#include -#include -#include -#include -#include -#include -#include "test.h" - -void setup(); -void cleanup(); - -char *TCID = "fcntl28"; -int TST_TOTAL = 1; - -char fname[255]; -int fd; - -int main(int ac, char **av) -{ - int lc, expected_result = -1; - /*************************************************************** - * parse standard options - ***************************************************************/ - tst_parse_opts(ac, av, NULL, NULL); - - /*************************************************************** - * perform global setup for test - ***************************************************************/ - setup(); - - expected_result = -1; - - /*************************************************************** - * check looping state if -c option given - ***************************************************************/ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - -#ifdef F_SETLEASE - /* - * Call fcntl(2) with F_SETLEASE & F_RDLCK argument on fname - */ - TEST(fcntl(fd, F_SETLEASE, F_RDLCK)); - - /* check return code */ - if (TEST_RETURN == expected_result) { - tst_resm(TPASS, - "fcntl(fd, F_SETLEASE, F_RDLCK) succeeded"); - } else { - tst_resm(TFAIL, "fcntl(%s, F_SETLEASE, F_RDLCK)" - " failed with errno %d : %s", fname, - TEST_ERRNO, strerror(TEST_ERRNO)); - } - -#else - tst_resm(TINFO, "F_SETLEASE not defined, skipping test"); -#endif - } - - /*************************************************************** - * cleanup and exit - ***************************************************************/ - cleanup(); - tst_exit(); - -} - -/*************************************************************** - * setup() - performs all ONE TIME setup for this test. - ***************************************************************/ -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - sprintf(fname, "tfile_%d", getpid()); - if ((fd = open(fname, O_WRONLY | O_CREAT, 0222)) == -1) { - tst_brkm(TBROK, cleanup, - "open(%s, O_WRONLY|O_CREAT,0222) Failed, errno=%d : %s", - fname, errno, strerror(errno)); - } -} - -/*************************************************************** - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - ***************************************************************/ -void cleanup(void) -{ - - /* close the file we've had open */ - if (close(fd) == -1) { - tst_resm(TWARN, "close(%s) Failed, errno=%d : %s", fname, errno, - strerror(errno)); - } - - tst_rmdir(); - -} diff --git a/testcases/kernel/syscalls/fcntl/fcntl29.c b/testcases/kernel/syscalls/fcntl/fcntl29.c index c94c9e74..836ca30e 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl29.c +++ b/testcases/kernel/syscalls/fcntl/fcntl29.c @@ -1,102 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Fujitsu Ltd. * Author: Xiaoguang Wang - * - * 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. + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -/* - * Description: - * Verify that, - * Basic test for fcntl(2) using F_DUPFD_CLOEXEC argument. +/*\ + * Basic test for fcntl(2) using F_DUPFD_CLOEXEC and getting FD_CLOEXEC. */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" #include "lapi/fcntl.h" +#include -char *TCID = "fcntl29"; -int TST_TOTAL = 1; - -static void setup(void); -static void cleanup(void); - -static int test_fd; - -int main(int ac, char **av) -{ - int lc, dup_fd; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - TEST(fcntl(test_fd, F_DUPFD_CLOEXEC, 0)); - if (TEST_RETURN < 0) { - tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl " - "test F_DUPFD_CLOEXEC failed"); - } - dup_fd = TEST_RETURN; - - TEST(fcntl(dup_fd, F_GETFD)); - if (TEST_RETURN < 0) { - SAFE_CLOSE(cleanup, dup_fd); - tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl " - "test F_GETFD failed"); - } - - if (TEST_RETURN & FD_CLOEXEC) { - tst_resm(TPASS, "fcntl test " - "F_DUPFD_CLOEXEC success"); - } else { - tst_resm(TFAIL, "fcntl test " - "F_DUPFD_CLOEXEC fail"); - } - - SAFE_CLOSE(cleanup, dup_fd); - } - - cleanup(); - tst_exit(); -} +static int fd; static void setup(void) { - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - tst_tmpdir(); - - TEST_PAUSE; - - test_fd = SAFE_CREAT(cleanup, "testfile", 0644); + fd = SAFE_CREAT("testfile", 0644); } static void cleanup(void) { - if (test_fd > 0) - SAFE_CLOSE(NULL, test_fd); - - tst_rmdir(); + if (fd > 0) + SAFE_CLOSE(fd); } + +static void run(void) +{ + TST_EXP_FD(fcntl(fd, F_DUPFD_CLOEXEC, 0)); + int dup_fd = TST_RET; + + TST_EXP_POSITIVE(fcntl(dup_fd, F_GETFD), "fcntl test F_GETFD"); + if (TST_RET & FD_CLOEXEC) + tst_res(TPASS, "fcntl() set FD_CLOEXEC"); + else + tst_res(TFAIL, "fcntl() did not set FD_CLOEXEC"); + + SAFE_CLOSE(dup_fd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/fcntl/fcntl30.c b/testcases/kernel/syscalls/fcntl/fcntl30.c index 64bbb9e3..4d1b7e62 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl30.c +++ b/testcases/kernel/syscalls/fcntl/fcntl30.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Fujitsu Ltd. * Author: Xiaoguang Wang @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, fetching and changing the capacity of a pipe works as * expected with fcntl(2) syscall using F_GETPIPE_SZ, F_SETPIPE_SZ arguments. */ diff --git a/testcases/kernel/syscalls/fcntl/fcntl31.c b/testcases/kernel/syscalls/fcntl/fcntl31.c index f6f625e8..f5c4f839 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl31.c +++ b/testcases/kernel/syscalls/fcntl/fcntl31.c @@ -44,13 +44,11 @@ static void cleanup(void); static void setown_pid_test(void); static void setown_pgrp_test(void); -#if defined(HAVE_STRUCT_F_OWNER_EX) static void setownex_tid_test(void); static void setownex_pid_test(void); static void setownex_pgrp_test(void); static struct f_owner_ex orig_own_ex; -#endif static void signal_parent(void); static void check_io_signal(char *des); @@ -68,9 +66,7 @@ static int pipe_fds[2]; static void (*testfunc[])(void) = { setown_pid_test, setown_pgrp_test, -#if defined(HAVE_STRUCT_F_OWNER_EX) setownex_tid_test, setownex_pid_test, setownex_pgrp_test -#endif }; char *TCID = "fcntl31"; @@ -122,14 +118,12 @@ static void setup(void) if (pgrp_pid < 0) tst_brkm(TBROK | TERRNO, cleanup, "getpgid() failed"); -#if defined(HAVE_STRUCT_F_OWNER_EX) /* get original f_owner_ex info */ TEST(fcntl(test_fd, F_GETOWN_EX, &orig_own_ex)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl get original f_owner_ex info failed"); } -#endif /* get original pid info */ TEST(fcntl(test_fd, F_GETOWN)); @@ -183,7 +177,6 @@ static void setown_pgrp_test(void) } } -#if defined(HAVE_STRUCT_F_OWNER_EX) static void setownex_cleanup(void) { TEST(fcntl(test_fd, F_SETOWN_EX, &orig_own_ex)); @@ -245,7 +238,6 @@ static void setownex_pgrp_test(void) setownex_cleanup(); } -#endif static void test_set_and_get_sig(int sig, char *des) { diff --git a/testcases/kernel/syscalls/fcntl/fcntl33.c b/testcases/kernel/syscalls/fcntl/fcntl33.c index 3c6a38b8..e0a2009b 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl33.c +++ b/testcases/kernel/syscalls/fcntl/fcntl33.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015 Fujitsu Ltd. * Author: Guangwen Feng diff --git a/testcases/kernel/syscalls/fcntl/fcntl34.c b/testcases/kernel/syscalls/fcntl/fcntl34.c index 45e693fe..7c3cea05 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl34.c +++ b/testcases/kernel/syscalls/fcntl/fcntl34.c @@ -125,7 +125,6 @@ static void test01(void) } static struct tst_test test = { - .min_kver = "3.15.0", .needs_tmpdir = 1, .test_all = test01, .setup = setup diff --git a/testcases/kernel/syscalls/fcntl/fcntl36.c b/testcases/kernel/syscalls/fcntl/fcntl36.c index e84b7ed0..19a5a030 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl36.c +++ b/testcases/kernel/syscalls/fcntl/fcntl36.c @@ -387,7 +387,7 @@ static void tests(unsigned int i) } static struct tst_test test = { - .min_kver = "3.15", + .timeout = 9, .needs_tmpdir = 1, .test = tests, .tcnt = ARRAY_SIZE(tcases), diff --git a/testcases/kernel/syscalls/fcntl/fcntl38.c b/testcases/kernel/syscalls/fcntl/fcntl38.c index 2f1b0229..d867bf87 100755 --- a/testcases/kernel/syscalls/fcntl/fcntl38.c +++ b/testcases/kernel/syscalls/fcntl/fcntl38.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 CTERA Networks. All Rights Reserved. * diff --git a/testcases/kernel/syscalls/fcntl/fcntl39.c b/testcases/kernel/syscalls/fcntl/fcntl39.c index 973b6a65..1142e1a6 100644 --- a/testcases/kernel/syscalls/fcntl/fcntl39.c +++ b/testcases/kernel/syscalls/fcntl/fcntl39.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2021 CTERA Networks. All Rights Reserved. * @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that dnotify DN_RENAME event is reported only on rename inside same parent. */ diff --git a/testcases/kernel/syscalls/fcntl/fcntl40.c b/testcases/kernel/syscalls/fcntl/fcntl40.c new file mode 100644 index 00000000..e90525fe --- /dev/null +++ b/testcases/kernel/syscalls/fcntl/fcntl40.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE Wei Gao + */ + +/*\ + * Basic test for fcntl using F_CREATED_QUERY. + * Verify if the fcntl() syscall is recognizing whether a file has been + * created or not via O_CREAT when O_CLOEXEC is also used. + * + * Test is based on a kernel selftests commit d0fe8920cbe4. + */ + +#include "lapi/fcntl.h" +#include "tst_test.h" + +#define TEST_NAME "LTP_FCNTL_CREATED_QUERY_TEST" + +static void verify_fcntl(void) +{ + int fd; + + fd = SAFE_OPEN("/dev/null", O_RDONLY | O_CLOEXEC); + + /* We didn't create "/dev/null". */ + TST_EXP_EQ_LI(fcntl(fd, F_CREATED_QUERY, 0), 0); + SAFE_CLOSE(fd); + + fd = SAFE_OPEN(TEST_NAME, O_CREAT | O_RDONLY | O_CLOEXEC, 0600); + + TST_EXP_EQ_LI(fcntl(fd, F_CREATED_QUERY, 0), 1); + SAFE_CLOSE(fd); + + fd = SAFE_OPEN(TEST_NAME, O_RDONLY | O_CLOEXEC); + + /* We're opening it again, so no positive creation check. */ + TST_EXP_EQ_LI(fcntl(fd, F_CREATED_QUERY, 0), 0); + SAFE_CLOSE(fd); + SAFE_UNLINK(TEST_NAME); +} + +static struct tst_test test = { + .test_all = verify_fcntl, + .needs_tmpdir = 1, + .min_kver = "6.12", +}; diff --git a/testcases/kernel/syscalls/fdatasync/fdatasync03.c b/testcases/kernel/syscalls/fdatasync/fdatasync03.c index 5f3e0c96..f1b5f0fb 100755 --- a/testcases/kernel/syscalls/fdatasync/fdatasync03.c +++ b/testcases/kernel/syscalls/fdatasync/fdatasync03.c @@ -53,6 +53,7 @@ static void verify_fdatasync(void) } static struct tst_test test = { + .timeout = 15, .needs_root = 1, .mount_device = 1, .all_filesystems = 1, diff --git a/testcases/kernel/syscalls/fgetxattr/fgetxattr01.c b/testcases/kernel/syscalls/fgetxattr/fgetxattr01.c index 52e6e44a..eefddd82 100755 --- a/testcases/kernel/syscalls/fgetxattr/fgetxattr01.c +++ b/testcases/kernel/syscalls/fgetxattr/fgetxattr01.c @@ -134,6 +134,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 10, .setup = setup, .test = verify_fgetxattr, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/file_attr/.gitignore b/testcases/kernel/syscalls/file_attr/.gitignore new file mode 100644 index 00000000..3fcb9004 --- /dev/null +++ b/testcases/kernel/syscalls/file_attr/.gitignore @@ -0,0 +1,4 @@ +file_attr01 +file_attr02 +file_attr03 +file_attr04 diff --git a/testcases/kernel/syscalls/file_attr/Makefile b/testcases/kernel/syscalls/file_attr/Makefile new file mode 100644 index 00000000..3b19b0ce --- /dev/null +++ b/testcases/kernel/syscalls/file_attr/Makefile @@ -0,0 +1,8 @@ +# Copyright (c) 2025 - Linaro Limited. All rights reserved. +# SPDX-License-Identifier: GPL-2.0-or-later + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/file_attr/file_attr01.c b/testcases/kernel/syscalls/file_attr/file_attr01.c new file mode 100644 index 00000000..c9c9288a --- /dev/null +++ b/testcases/kernel/syscalls/file_attr/file_attr01.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that `file_getattr` and `file_setattr` syscalls are raising the + * correct errors according to the invalid input arguments. In particular: + * + * - EBADFD: Invalid file descriptor. + * - ENOENT: File doesn't exist + * - EFAULT: File name is NULL + * - EFAULT: File attributes is NULL + * - EINVAL: File attributes size is zero + * - E2BIG: File attributes size is too big + * - EINVAL: Invalid AT flags + */ + +#include +#include "tst_test.h" +#include "lapi/fs.h" +#include "lapi/fcntl.h" + +#define MNTPOINT "mntpoint" +#define FILENAME "ltp_file" +#define NO_FILENAME "ltp_file_doesnt_exist" + +static int valid_dfd = -1; +static int invalid_dfd = -1; +static char *valid_filename; +static char *invalid_filename; +static char *null_ptr; +static size_t zero; +static size_t small_usize; +static size_t valid_usize; +static size_t big_usize; +static struct file_attr *valid_file_attr; + +static struct tcase { + int *dfd; + char **filename; + struct file_attr **ufattr; + size_t *usize; + int at_flags; + int exp_errno; + char *msg; +} tcases[] = { + { + .dfd = &invalid_dfd, + .filename = &valid_filename, + .ufattr = &valid_file_attr, + .usize = &valid_usize, + .exp_errno = EBADF, + .msg = "Invalid file descriptor", + }, + { + .dfd = &valid_dfd, + .filename = &invalid_filename, + .ufattr = &valid_file_attr, + .usize = &valid_usize, + .exp_errno = ENOENT, + .msg = "File doesn't exist", + }, + { + .dfd = &valid_dfd, + .filename = &null_ptr, + .ufattr = &valid_file_attr, + .usize = &valid_usize, + .exp_errno = EFAULT, + .msg = "Filename is NULL", + }, + { + .dfd = &valid_dfd, + .filename = &valid_filename, + .ufattr = (struct file_attr **)(&null_ptr), + .usize = &valid_usize, + .exp_errno = EFAULT, + .msg = "File attributes is NULL", + }, + { + .dfd = &valid_dfd, + .filename = &valid_filename, + .ufattr = &valid_file_attr, + .usize = &zero, + .exp_errno = EINVAL, + .msg = "File attributes size is zero", + }, + { + .dfd = &valid_dfd, + .filename = &valid_filename, + .ufattr = &valid_file_attr, + .usize = &small_usize, + .exp_errno = EINVAL, + .msg = "File attributes size is too small", + }, + { + .dfd = &valid_dfd, + .filename = &valid_filename, + .ufattr = &valid_file_attr, + .usize = &big_usize, + .exp_errno = E2BIG, + .msg = "File attributes size is too big", + }, + { + .dfd = &valid_dfd, + .filename = &valid_filename, + .ufattr = &valid_file_attr, + .usize = &valid_usize, + .at_flags = -1, + .exp_errno = EINVAL, + .msg = "Invalid AT flags", + }, +}; + +static void run(unsigned int i) +{ + struct tcase *tc = &tcases[i]; + + if (tst_variant) { + TST_EXP_FAIL(file_getattr( + *tc->dfd, *tc->filename, + *tc->ufattr, *tc->usize, + tc->at_flags), + tc->exp_errno, + "%s", tc->msg); + } else { + TST_EXP_FAIL(file_setattr( + *tc->dfd, *tc->filename, + *tc->ufattr, *tc->usize, + tc->at_flags), + tc->exp_errno, + "%s", tc->msg); + } +} + +static void setup(void) +{ + valid_dfd = SAFE_OPEN(MNTPOINT, O_RDONLY); + + SAFE_CHDIR(MNTPOINT); + SAFE_TOUCH(FILENAME, 0777, NULL); + SAFE_CHDIR(".."); + + valid_usize = FILE_ATTR_SIZE_LATEST; + small_usize = FILE_ATTR_SIZE_VER0 - 1; + big_usize = SAFE_SYSCONF(_SC_PAGESIZE) + 100; +} + +static void cleanup(void) +{ + if (valid_dfd != -1) + SAFE_CLOSE(valid_dfd); +} + +static struct tst_test test = { + .test = run, + .setup = setup, + .cleanup = cleanup, + .tcnt = ARRAY_SIZE(tcases), + .mntpoint = MNTPOINT, + .needs_root = 1, + .mount_device = 1, + .all_filesystems = 1, + .test_variants = 2, + .skip_filesystems = (const char *const []) { + "fuse", + "ntfs", + "vfat", + "exfat", + NULL + }, + .bufs = (struct tst_buffers []) { + {&valid_filename, .str = FILENAME}, + {&invalid_filename, .str = NO_FILENAME}, + {&valid_file_attr, .size = sizeof(struct file_attr)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/file_attr/file_attr02.c b/testcases/kernel/syscalls/file_attr/file_attr02.c new file mode 100644 index 00000000..f6625985 --- /dev/null +++ b/testcases/kernel/syscalls/file_attr/file_attr02.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that `file_getattr` is correctly reading filesystems additional + * attributes. We are running test on XFS only, since it's the only filesystem + * currently implementing the features we need. + */ + +#include +#include "tst_test.h" +#include "lapi/fs.h" + +#define MNTPOINT "mntpoint" +#define FILENAME "ltp_file" +#define BLOCKS 128 +#define PROJID 16 + +static int fd = -1; +static int dfd = -1; +static struct fsxattr xattr; +static struct file_attr *attr; + +static void run(void) +{ + memset(attr, 0, sizeof(*attr)); + + TST_EXP_PASS(file_getattr( + dfd, FILENAME, + attr, FILE_ATTR_SIZE_LATEST, + 0)); + + TST_EXP_EQ_LI(attr->fa_xflags, xattr.fsx_xflags); + TST_EXP_EQ_LI(attr->fa_extsize, xattr.fsx_extsize); + TST_EXP_EQ_LI(attr->fa_cowextsize, xattr.fsx_cowextsize); + TST_EXP_EQ_LI(attr->fa_nextents, xattr.fsx_nextents); + TST_EXP_EQ_LI(attr->fa_projid, PROJID); + TST_EXP_EQ_LI(attr->fa_projid, xattr.fsx_projid); +} + +static void setup(void) +{ + struct stat statbuf; + + SAFE_MKDIR(MNTPOINT, 0755); + TEST(mount(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL)); + + if (TST_RET == -1 && TST_ERR == EOPNOTSUPP) + tst_brk(TCONF, "Kernel does not support XFS reflinks"); + + if (TST_RET) + tst_brk(TBROK | TTERRNO, "Mount failed"); + + SAFE_STAT(MNTPOINT, &statbuf); + + dfd = SAFE_OPEN(MNTPOINT, O_RDONLY); + fd = SAFE_CREAT(MNTPOINT "/" FILENAME, 0777); + + SAFE_IOCTL(fd, FS_IOC_FSGETXATTR, &xattr); + + xattr.fsx_xflags |= FS_XFLAG_EXTSIZE; + xattr.fsx_xflags |= FS_XFLAG_COWEXTSIZE; + xattr.fsx_extsize = BLOCKS * statbuf.st_blksize; + xattr.fsx_cowextsize = BLOCKS * statbuf.st_blksize; + xattr.fsx_projid = PROJID; + + SAFE_IOCTL(fd, FS_IOC_FSSETXATTR, &xattr); + + /* this will force at least one extent to be allocated */ + SAFE_WRITE(SAFE_WRITE_ALL, fd, "a", 1); + + SAFE_IOCTL(fd, FS_IOC_FSGETXATTR, &xattr); + SAFE_CLOSE(fd); +} + +static void cleanup(void) +{ + if (fd != -1) + SAFE_CLOSE(fd); + + if (dfd != -1) + SAFE_CLOSE(dfd); + + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .format_device = 1, + .filesystems = (struct tst_fs []) { + { + .type = "xfs", + .mkfs_opts = (const char *const[]){ + "-m", "reflink=1", NULL + }, + }, + {} + }, + .bufs = (struct tst_buffers []) { + {&attr, .size = sizeof(struct file_attr)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/file_attr/file_attr03.c b/testcases/kernel/syscalls/file_attr/file_attr03.c new file mode 100644 index 00000000..9ad77904 --- /dev/null +++ b/testcases/kernel/syscalls/file_attr/file_attr03.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that `file_setattr` is correctly setting filesystems additional + * attributes. We are running test on XFS only, since it's the only filesystem + * currently implementing the features we need. + */ + +#include "tst_test.h" +#include "lapi/fs.h" + +#define MNTPOINT "mntpoint" +#define FILEPATH (MNTPOINT "/ltp_file") +#define BLOCKS 1024 +#define PROJID 16 + +static int fd = -1; +static int block_size; +static struct fsxattr xattr; +static struct file_attr *attr; + +static void run(void) +{ + fd = SAFE_CREAT(FILEPATH, 0777); + + TST_EXP_PASS(file_setattr(AT_FDCWD, FILEPATH, + attr, FILE_ATTR_SIZE_LATEST, 0)); + + SAFE_IOCTL(fd, FS_IOC_FSGETXATTR, &xattr); + SAFE_CLOSE(fd); + + TST_EXP_EQ_LI(xattr.fsx_xflags & FS_XFLAG_EXTSIZE, FS_XFLAG_EXTSIZE); + TST_EXP_EQ_LI(xattr.fsx_xflags & FS_XFLAG_COWEXTSIZE, FS_XFLAG_COWEXTSIZE); + TST_EXP_EQ_LI(xattr.fsx_extsize, BLOCKS * block_size); + TST_EXP_EQ_LI(xattr.fsx_cowextsize, BLOCKS * block_size); + TST_EXP_EQ_LI(xattr.fsx_projid, PROJID); + + SAFE_UNLINK(FILEPATH); +} + +static void setup(void) +{ + block_size = tst_dev_block_size(MNTPOINT); + + attr->fa_xflags |= FS_XFLAG_EXTSIZE; + attr->fa_xflags |= FS_XFLAG_COWEXTSIZE; + attr->fa_extsize = BLOCKS * block_size; + attr->fa_cowextsize = BLOCKS * block_size; + attr->fa_projid = PROJID; +} + +static void cleanup(void) +{ + if (fd != -1) + SAFE_CLOSE(fd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .mntpoint = MNTPOINT, + .needs_root = 1, + .mount_device = 1, + .filesystems = (struct tst_fs []) { + {.type = "xfs"}, + {} + }, + .bufs = (struct tst_buffers []) { + {&attr, .size = sizeof(struct file_attr)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/file_attr/file_attr04.c b/testcases/kernel/syscalls/file_attr/file_attr04.c new file mode 100644 index 00000000..364fad08 --- /dev/null +++ b/testcases/kernel/syscalls/file_attr/file_attr04.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that `file_getattr` and `file_setattr` are correctly raising an error + * when the wrong file descriptors types are passed to them. + */ + +#include "tst_test.h" +#include "lapi/fs.h" + +#define FILENAME "ltp_file" + +static struct file_attr *attr; + +static void test_invalid_fd(struct tst_fd *fd) +{ + if (fd->type == TST_FD_DIR || fd->type == TST_FD_OPEN_TREE) { + tst_res(TCONF, "Skipping DIR fd"); + return; + } + + memset(attr, 0, sizeof(*attr)); + + if (tst_variant) { + TST_EXP_FAIL(file_getattr( + fd->fd, FILENAME, + attr, FILE_ATTR_SIZE_LATEST, + 0), ENOTDIR); + } else { + TST_EXP_FAIL(file_setattr( + fd->fd, FILENAME, + attr, FILE_ATTR_SIZE_LATEST, + 0), ENOTDIR); + } +} + +static void run(void) +{ + TST_FD_FOREACH(fd) { + tst_res(TINFO, "%s -> ...", tst_fd_desc(&fd)); + test_invalid_fd(&fd); + } +} + +static void setup(void) +{ + SAFE_TOUCH(FILENAME, 0640, NULL); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .test_variants = 2, + .needs_tmpdir = 1, + .bufs = (struct tst_buffers []) { + {&attr, .size = sizeof(struct file_attr)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/finit_module/.gitignore b/testcases/kernel/syscalls/finit_module/.gitignore index 5fcafdb3..3aebad33 100755 --- a/testcases/kernel/syscalls/finit_module/.gitignore +++ b/testcases/kernel/syscalls/finit_module/.gitignore @@ -1,3 +1,8 @@ /finit_module01 /finit_module02 /*.ko +/*.cmd +/*.order +/*.mod.c +/Module.symvers +modules.livepatch diff --git a/testcases/kernel/syscalls/finit_module/finit_module.c b/testcases/kernel/syscalls/finit_module/finit_module.c index 78d03b89..a7b1e43c 100755 --- a/testcases/kernel/syscalls/finit_module/finit_module.c +++ b/testcases/kernel/syscalls/finit_module/finit_module.c @@ -13,6 +13,8 @@ #include #include +#define DIRNAME "dummy_finit" + static char status[20]; module_param_string(status, status, 20, 0444); @@ -23,14 +25,14 @@ static int dummy_init(void) if (!strcmp(status, "invalid")) return -EINVAL; - proc_dummy = proc_mkdir("dummy", 0); + proc_dummy = proc_mkdir(DIRNAME, 0); return 0; } module_init(dummy_init); static void dummy_exit(void) { - remove_proc_entry("dummy", 0); + remove_proc_entry(DIRNAME, 0); } module_exit(dummy_exit); diff --git a/testcases/kernel/syscalls/finit_module/finit_module01.c b/testcases/kernel/syscalls/finit_module/finit_module01.c index 1929c30f..397d5a1a 100755 --- a/testcases/kernel/syscalls/finit_module/finit_module01.c +++ b/testcases/kernel/syscalls/finit_module/finit_module01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic finit_module() tests. * * [Algorithm] @@ -13,18 +11,22 @@ * Inserts a simple module after opening and mmaping the module file. */ +#include #include #include "lapi/init_module.h" #include "tst_module.h" #define MODULE_NAME "finit_module.ko" -static int fd; +static int fd, sig_enforce; static char *mod_path; static void setup(void) { + if (tst_module_signature_enforced()) + sig_enforce = 1; + tst_module_exists(MODULE_NAME, &mod_path); fd = SAFE_OPEN(mod_path, O_RDONLY|O_CLOEXEC); @@ -32,6 +34,11 @@ static void setup(void) static void run(void) { + if (sig_enforce == 1) { + TST_EXP_FAIL(finit_module(fd, "status=valid", 0), EKEYREJECTED); + return; + } + TST_EXP_PASS(finit_module(fd, "status=valid", 0)); if (!TST_PASS) return; diff --git a/testcases/kernel/syscalls/finit_module/finit_module02.c b/testcases/kernel/syscalls/finit_module/finit_module02.c index 223d9b38..78dc7410 100755 --- a/testcases/kernel/syscalls/finit_module/finit_module02.c +++ b/testcases/kernel/syscalls/finit_module/finit_module02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic finit_module() failure tests. * * [Algorithm] @@ -14,6 +12,7 @@ */ #include +#include #include #include "lapi/init_module.h" #include "tst_module.h" @@ -25,7 +24,7 @@ static char *mod_path; static int fd, fd_zero, fd_invalid = -1, fd_dir; -static int kernel_lockdown, secure_boot; +static int kernel_lockdown, secure_boot, sig_enforce; static struct tst_cap cap_req = TST_CAP(TST_CAP_REQ, CAP_SYS_MODULE); static struct tst_cap cap_drop = TST_CAP(TST_CAP_DROP, CAP_SYS_MODULE); @@ -59,21 +58,16 @@ static void dir_setup(struct tcase *tc) } static struct tcase tcases[] = { - {"invalid-fd", &fd_invalid, "", O_RDONLY | O_CLOEXEC, 0, 0, 0, 0, - bad_fd_setup}, + {"invalid-fd", &fd_invalid, "", O_RDONLY | O_CLOEXEC, 0, 0, 0, 0, bad_fd_setup}, {"zero-fd", &fd_zero, "", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, 0, NULL}, {"null-param", &fd, NULL, O_RDONLY | O_CLOEXEC, 0, 0, EFAULT, 1, NULL}, - {"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0, - EINVAL, 1, NULL}, - {"invalid-flags", &fd, "", O_RDONLY | O_CLOEXEC, -1, 0, EINVAL, 0, - NULL}, + {"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, 1, NULL}, + {"invalid-flags", &fd, "", O_RDONLY | O_CLOEXEC, -1, 0, EINVAL, 0, NULL}, {"no-perm", &fd, "", O_RDONLY | O_CLOEXEC, 0, 1, EPERM, 0, NULL}, - {"module-exists", &fd, "", O_RDONLY | O_CLOEXEC, 0, 0, EEXIST, 1, - NULL}, - {"file-not-readable", &fd, "", O_WRONLY | O_CLOEXEC, 0, 0, EBADF, 0, - NULL}, - {"file-readwrite", &fd, "", O_RDWR | O_CLOEXEC, 0, 0, ETXTBSY, 0, - NULL}, + {"module-exists", &fd, "", O_RDONLY | O_CLOEXEC, 0, 0, EEXIST, 1, NULL}, + {"module-unsigned", &fd, "", O_RDONLY | O_CLOEXEC, 0, 0, EKEYREJECTED, 1, NULL}, + {"file-not-readable", &fd, "", O_WRONLY | O_CLOEXEC, 0, 0, EBADF, 0, NULL}, + {"file-readwrite", &fd, "", O_RDWR | O_CLOEXEC, 0, 0, ETXTBSY, 0, NULL}, {"directory", &fd_dir, "", O_RDONLY | O_CLOEXEC, 0, 0, 0, 0, dir_setup}, }; @@ -81,6 +75,9 @@ static void setup(void) { unsigned long int i; + if (tst_module_signature_enforced()) + sig_enforce = 1; + tst_module_exists(MODULE_NAME, &mod_path); kernel_lockdown = tst_lockdown_enabled() > 0; @@ -109,6 +106,16 @@ static void run(unsigned int n) return; } + if ((sig_enforce == 1) && (tc->exp_errno != EKEYREJECTED)) { + tst_res(TCONF, "module signature is enforced, skipping %s", tc->name); + return; + } + + if ((sig_enforce != 1) && (tc->exp_errno == EKEYREJECTED)) { + tst_res(TCONF, "module signature is not enforced, skipping %s", tc->name); + return; + } + fd = SAFE_OPEN(mod_path, tc->open_flags); if (tc->cap) diff --git a/testcases/kernel/syscalls/flock/.gitignore b/testcases/kernel/syscalls/flock/.gitignore index c8cb0fc5..9bac582e 100755 --- a/testcases/kernel/syscalls/flock/.gitignore +++ b/testcases/kernel/syscalls/flock/.gitignore @@ -3,3 +3,4 @@ /flock03 /flock04 /flock06 +/flock07 diff --git a/testcases/kernel/syscalls/flock/flock01.c b/testcases/kernel/syscalls/flock/flock01.c index 6b651c90..c8e5aac1 100755 --- a/testcases/kernel/syscalls/flock/flock01.c +++ b/testcases/kernel/syscalls/flock/flock01.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. + * Copyright (c) Linux Test Project, 2007-2018 * Author: Vatsal Avasthi - * - * Test Description: - * This test verifies that flock() succeeds with all kind of locks. + */ + +/*\ + * Basic test for flock(2), uses LOCK_SH, LOCK_UN, LOCK_EX locks. */ #include diff --git a/testcases/kernel/syscalls/flock/flock02.c b/testcases/kernel/syscalls/flock/flock02.c index 07f11ec3..a8ca7c88 100755 --- a/testcases/kernel/syscalls/flock/flock02.c +++ b/testcases/kernel/syscalls/flock/flock02.c @@ -1,15 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. + * Copyright (c) Linux Test Project, 2002-2018 * Author: Vatsal Avasthi + */ + +/*\ + * Verify flock(2) returns -1 and set proper errno: * - * Test Description: - * 1) flock() returns -1 and sets error number to EBADF if the file descriptor - * is invalid. - * 2) flock() returns -1 and sets error number to EINVAL if the argument - * operation does not include LOCK_SH,LOCK_EX,LOCK_UN. - * 3) flock() returns -1 and sets error number to EINVAL if an invalid - * combination of locking modes is used i.e LOCK_SH with LOCK_EX + * - EBADF if the file descriptor is invalid + * - EINVAL if the argument operation does not include LOCK_SH,LOCK_EX,LOCK_UN + * - EINVAL if an invalid combination of locking modes is used i.e LOCK_SH with LOCK_EX + * - EWOULDBLOCK if the file is locked and the LOCK_NB flag was selected */ #include @@ -28,13 +30,21 @@ static struct tcase { {&badfd, LOCK_SH, EBADF}, {&fd, LOCK_NB, EINVAL}, {&fd, LOCK_SH | LOCK_EX, EINVAL}, + {&fd, LOCK_NB | LOCK_EX, EWOULDBLOCK} }; -static void verify_flock(unsigned n) +static void verify_flock(unsigned int n) { + int fd2 = -1; struct tcase *tc = &tcases[n]; fd = SAFE_OPEN("testfile", O_RDWR); + + if (tc->exp_err == EWOULDBLOCK) { + fd2 = SAFE_OPEN("testfile", O_RDWR); + flock(fd2, LOCK_EX); + } + TEST(flock(*tc->fd, tc->operation)); if (TST_RET == 0) { tst_res(TFAIL | TTERRNO, "flock() succeeded unexpectedly"); @@ -50,6 +60,8 @@ static void verify_flock(unsigned n) } SAFE_CLOSE(fd); + if (fd2 != -1) + SAFE_CLOSE(fd2); } static void setup(void) diff --git a/testcases/kernel/syscalls/flock/flock03.c b/testcases/kernel/syscalls/flock/flock03.c index 92e21a6a..be6475f1 100755 --- a/testcases/kernel/syscalls/flock/flock03.c +++ b/testcases/kernel/syscalls/flock/flock03.c @@ -1,18 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2002 - * Author: Cyril Hrubis + * Copyright (c) 2012-2015 Cyril Hrubis + * Copyright (c) Linux Test Project, 2002-2018 + */ + +/*\ + * Verify that flock(2) cannot unlock a file locked by another task. * - * Test Description: - * This test verifies that flock() cannot unlock a file locked by another - * task. - * - * Test Steps: - * Fork a child processes The parent flocks a file with LOCK_EX Child waits - * for that to happen, then checks to make sure it is locked. Child then - * tries to unlock the file. If the unlock succeeds, the child attempts to - * lock the file with LOCK_EX. The test passes if the child is able to lock - * the file. + * Fork a child processes. The parent flocks a file with LOCK_EX. Child waits + * for that to happen, then checks to make sure it is locked. Child then + * tries to unlock the file. If the unlock succeeds, the child attempts to + * lock the file with LOCK_EX. The test passes if the child is able to lock + * the file. */ #include diff --git a/testcases/kernel/syscalls/flock/flock04.c b/testcases/kernel/syscalls/flock/flock04.c index 89d1949c..da354a65 100755 --- a/testcases/kernel/syscalls/flock/flock04.c +++ b/testcases/kernel/syscalls/flock/flock04.c @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. + * Copyright (c) Linux Test Project, 2003-2021 * Author: Vatsal Avasthi + */ + +/*\ + * Test verifies that flock() behavior with different locking combinations along + * with LOCK_SH and LOCK_EX: * - * Test Description: - * This test verifies that flock() behavior with different locking - * combinations along with LOCK_SH and LOCK_EX: - * 1) flock() succeeded in acquiring shared lock on shared lock file. - * 2) flock() failed to acquire exclusive lock on shared lock file. - * 3) flock() failed to acquire shared lock on exclusive lock file. - * 4) flock() failed to acquire exclusive lock on exclusive lock file. + * - flock() succeeded in acquiring shared lock on shared lock file. + * - flock() failed to acquire exclusive lock on shared lock file. + * - flock() failed to acquire shared lock on exclusive lock file. + * - flock() failed to acquire exclusive lock on exclusive lock file. */ #include diff --git a/testcases/kernel/syscalls/flock/flock06.c b/testcases/kernel/syscalls/flock/flock06.c index f5d469a3..363dc5bf 100755 --- a/testcases/kernel/syscalls/flock/flock06.c +++ b/testcases/kernel/syscalls/flock/flock06.c @@ -1,17 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) Matthew Wilcox for Hewlett Packard 2003 + * Copyright (c) Linux Test Project, 2007-2018 * Author: Matthew Wilcox + */ + +/*\ + * Test verifies that flock locks held on one file descriptor conflict with + * flock locks held on a different file descriptor. * - * Test Description: - * This test verifies that flock locks held on one fd conflict with flock - * locks held on a different fd. - * - * Test Steps: - * The process opens two file descriptors on the same file. It acquires - * an exclusive flock on the first descriptor, checks that attempting to - * acquire an flock on the second descriptor fails. Then it removes the - * first descriptor's lock and attempts to acquire an exclusive lock on - * the second descriptor. + * The process opens two file descriptors on the same file. It acquires + * an exclusive flock on the first descriptor, checks that attempting to + * acquire an flock on the second descriptor fails. Then it removes the + * first descriptor's lock and attempts to acquire an exclusive lock on + * the second descriptor. */ #include diff --git a/testcases/kernel/syscalls/flock/flock07.c b/testcases/kernel/syscalls/flock/flock07.c new file mode 100644 index 00000000..6b6848b9 --- /dev/null +++ b/testcases/kernel/syscalls/flock/flock07.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Author: Yang Xu + * Copyright (c) 2024 Linux Test Project + */ + +/*\ + * Verify that flock(2) fails with errno EINTR when waiting to acquire a lock, + * and the call is interrupted by a signal. + */ + +#include +#include "tst_test.h" + +#define TEMPFILE "test_eintr" + +static int fd1 = -1, fd2 = -1; + +static void handler(int sig) +{ + tst_res(TINFO, "Received signal: %s (%d)", tst_strsig(sig), sig); +} + +static void setup(void) +{ + SAFE_TOUCH(TEMPFILE, 0777, NULL); + fd1 = SAFE_OPEN(TEMPFILE, O_RDWR); + fd2 = SAFE_OPEN(TEMPFILE, O_RDWR); +} + +static void cleanup(void) +{ + if (fd1 >= 0) + SAFE_CLOSE(fd1); + + if (fd2 >= 0) + SAFE_CLOSE(fd2); +} + +static void child_do(int fd) +{ + struct sigaction sa = {}; + + sa.sa_handler = handler; + SAFE_SIGEMPTYSET(&sa.sa_mask); + SAFE_SIGACTION(SIGUSR1, &sa, NULL); + + tst_res(TINFO, "Trying to acquire exclusive lock from child"); + TST_EXP_FAIL(flock(fd, LOCK_EX), EINTR); +} + +static void verify_flock(void) +{ + pid_t pid; + + TST_EXP_PASS(flock(fd1, LOCK_EX)); + + pid = SAFE_FORK(); + if (!pid) { + child_do(fd2); + exit(0); + } else { + sleep(1); + SAFE_KILL(pid, SIGUSR1); + SAFE_WAITPID(pid, NULL, 0); + } +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = verify_flock, + .needs_tmpdir = 1, + .needs_root = 1, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/fork/.gitignore b/testcases/kernel/syscalls/fork/.gitignore index b817e9c0..acab5f76 100755 --- a/testcases/kernel/syscalls/fork/.gitignore +++ b/testcases/kernel/syscalls/fork/.gitignore @@ -2,12 +2,10 @@ /fork03 /fork04 /fork05 -/fork06 /fork07 /fork08 /fork09 /fork10 -/fork11 -/fork12 /fork13 /fork14 +/fork_procs diff --git a/testcases/kernel/syscalls/fork/fork01.c b/testcases/kernel/syscalls/fork/fork01.c index 31ec5d4c..b030c73a 100755 --- a/testcases/kernel/syscalls/fork/fork01.c +++ b/testcases/kernel/syscalls/fork/fork01.c @@ -6,10 +6,8 @@ */ /*\ - *[Description] - * - * - fork returns without error - * - fork returns the pid of the child + * This test verifies that fork returns without error and that it returns the + * pid of the child. */ #include diff --git a/testcases/kernel/syscalls/fork/fork03.c b/testcases/kernel/syscalls/fork/fork03.c index c6381dd6..dabe0d49 100755 --- a/testcases/kernel/syscalls/fork/fork03.c +++ b/testcases/kernel/syscalls/fork/fork03.c @@ -6,8 +6,6 @@ */ /*\ - *[Description] - * * Check that child process can use a large text space and do a large number * of operations. In this situation, check for pid == 0 in child and check * for pid > 0 in parent after wait. diff --git a/testcases/kernel/syscalls/fork/fork04.c b/testcases/kernel/syscalls/fork/fork04.c index 5e5e42c4..ecbbc47f 100755 --- a/testcases/kernel/syscalls/fork/fork04.c +++ b/testcases/kernel/syscalls/fork/fork04.c @@ -1,328 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * 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 Test - Silicon Graphics, Inc. - * TEST IDENTIFIER : fork04 - * TEST TITLE : Child inheritance of Environment Variables after fork() - * PARENT DOCUMENT : frktds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 1 - * CPU TYPES : ALL - * AUTHOR : Kathy Olmsted - * CO-PILOT : Steve Shaw - * DATE STARTED : 06/17/92 - * INITIAL RELEASE : UNICOS 7.0 - * - * TEST CASES - * Test these environment variables correctly inherited by child: - * 1. TERM - * 2. NoTSetzWq - * 3. TESTPROG - * - * INPUT SPECIFICATIONS - * The standard options for system call tests are accepted. - * (See the parse_opts(3) man page). - * - * DURATION - * Terminates - with frequency and infinite modes. - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * ENVIRONMENTAL NEEDS - * No run-time environmental needs. - * - * DETAILED DESCRIPTION - * - * Setup: - * Setup signal handling. - * Make and change to a temporary directory. - * Pause for SIGUSR1 if option specified. - * Add TESTPROG variable to the environment - * - * Test: - * Loop if the proper options are given. - * fork() - * Check return code, if system call failed (return=-1) - * Log the errno - * CHILD: - * open a temp file - * Determine environment values and write to file - * close file containing test values. - * exit. - * PARENT: - * Wait for child to exit. - * Verify exit status - * Open file containing test values. - * For each test case: - * Read the value from the file. - * Determine and report PASS/FAIL result. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * Remove the temporary directory and exit. + * Copyright (c) Linux Test Project, 2001-2023 + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that parent process shares environ variables with the + * child and that child doesn't change parent's environ variables. */ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" -char *TCID = "fork04"; +#define ENV_KEY "LTP_FORK04" +#define ENV_VAL0 "PASS" +#define ENV_VAL1 "FAIL" -#define KIDEXIT 42 -#define MAX_LINE_LENGTH 256 -#define OUTPUT_FILE "env.out" -#define ENV_NOT_SET "getenv() does not find variable set" - -/* list of environment variables to test */ -char *environ_list[] = { "TERM", "NoTSetzWq", "TESTPROG" }; - -#define NUMBER_OF_ENVIRON (sizeof(environ_list)/sizeof(char *)) -int TST_TOTAL = NUMBER_OF_ENVIRON; - -static void cleanup(void) +static void run_child(void) { - tst_rmdir(); + const char *val; + + val = getenv(ENV_KEY); + if (!val) + tst_brk(TBROK, "Can't find %s environ variable", ENV_KEY); + + TST_EXP_EXPR(strcmp(ENV_VAL0, val) == 0, + "%s environ variable has been inherited by the child", + ENV_KEY); + + tst_res(TINFO, "Unset %s environ variable inside child", ENV_KEY); + + if (unsetenv(ENV_KEY) == -1) + tst_brk(TBROK, "Can't unset %s environ variable", ENV_KEY); + + TST_CHECKPOINT_WAKE_AND_WAIT(0); + + tst_res(TINFO, "Set %s=%s environ variable inside child", ENV_KEY, ENV_VAL1); + + SAFE_SETENV(ENV_KEY, ENV_VAL1, 0); + + TST_CHECKPOINT_WAKE(0); } -static void setup(void) +static void run(void) { + const char *val; - tst_sig(FORK, DEF_HANDLER, cleanup); - TEST_PAUSE; - tst_tmpdir(); + tst_res(TINFO, + "Set %s=%s environ variable inside parent", + ENV_KEY, ENV_VAL0); - /* add a variable to the environment */ - putenv("TESTPROG=FRKTCS04"); -} + SAFE_SETENV(ENV_KEY, ENV_VAL0, 0); -static void child_environment(void) -{ + tst_res(TINFO, "Spawning child"); - int fildes; - int index; - char msg[MAX_LINE_LENGTH]; - char *var; - - fildes = creat(OUTPUT_FILE, 0700); - - for (index = 0; index < (int)NUMBER_OF_ENVIRON; index++) { - memset(msg, 0, MAX_LINE_LENGTH); - - var = getenv(environ_list[index]); - if (var == NULL) - (void)sprintf(msg, "%s:%s", environ_list[index], - ENV_NOT_SET); - else - (void)sprintf(msg, "%s:%s", environ_list[index], var); - /* includes extra null chars */ - write(fildes, msg, sizeof(msg)); + if (!SAFE_FORK()) { + run_child(); + exit(0); } - close(fildes); -} + TST_CHECKPOINT_WAIT(0); -/* - * Compare parent env string to child's string. - * Each string is in the format: : - */ -static int cmp_env_strings(char *pstring, char *cstring) -{ - char *penv, *cenv, *pvalue, *cvalue; - - /* - * Break pstring into env and value - */ - penv = pstring; - pvalue = strchr(pstring, ':'); - if (pvalue == NULL) { - tst_resm(TBROK, - "internal error - parent's env string not in correct format:'%s'", - pstring); - return -1; + val = getenv(ENV_KEY); + if (!val) { + tst_res(TFAIL, + "%s environ variable has been unset inside parent", + ENV_KEY); } else { - *pvalue = '\0'; - pvalue++; - if (*pvalue == '\0') { - tst_resm(TBROK, - "internal error - missing parent's env value"); - return -1; - } + TST_EXP_EXPR(strcmp(ENV_VAL0, val) == 0, + "%s environ variable is still present inside parent", + ENV_KEY); } - /* - * Break cstring into env and value - */ - cenv = cstring; - cvalue = strchr(cstring, ':'); - if (cvalue == NULL) { - tst_resm(TBROK, - "internal error - parent's env string not in correct format:'%s'", - cstring); - return -1; - } else { - *cvalue = '\0'; - cvalue++; - if (*cvalue == '\0') { - tst_resm(TBROK, - "internal error - missing child's env value"); - return -1; - } - } + TST_CHECKPOINT_WAKE_AND_WAIT(0); - if (strcmp(penv, cenv) != 0) { - tst_resm(TBROK, "internal error - parent(%s) != child (%s) env", - penv, cenv); - return -1; + val = getenv(ENV_KEY); + if (!val) + tst_res(TFAIL, + "%s environ variable has been unset inside parent", + ENV_KEY); + else { + TST_EXP_EXPR(strcmp(ENV_VAL0, val) == 0, + "%s environ variable didn't change inside parent", + ENV_KEY); } - - if (strcmp(pvalue, cvalue) != 0) { - tst_resm(TFAIL, - "Env var %s changed after fork(), parent's %s, child's %s", - penv, pvalue, cvalue); - } else { - tst_resm(TPASS, "Env var %s unchanged after fork(): %s", - penv, cvalue); - } - return 0; - } -/*************************************************************** - * parent_environment - the parent side of the environment tests - * determine values for the variables - * read the values determined by the child - * compare values - ***************************************************************/ -void parent_environment(void) -{ - - int fildes; - char tmp_line[MAX_LINE_LENGTH]; - char parent_value[MAX_LINE_LENGTH]; - unsigned int index; - int ret; - char *var; - - fildes = SAFE_OPEN(cleanup, OUTPUT_FILE, O_RDWR); - for (index = 0; index < NUMBER_OF_ENVIRON; index++) { - ret = read(fildes, tmp_line, MAX_LINE_LENGTH); - if (ret == 0) { - tst_resm(TBROK, - "fork() test. parent_environment: failed to read from file with %d (%s)", - errno, strerror(errno)); - } else { - - var = getenv(environ_list[index]); - if (var == NULL) - sprintf(parent_value, "%s:%s", - environ_list[index], ENV_NOT_SET); - else - sprintf(parent_value, "%s:%s", - environ_list[index], var); - - cmp_env_strings(parent_value, tmp_line); - - } - } - - close(fildes); -} - -int main(int ac, char **av) -{ - int lc; - int kid_status; - int wait_status; - int fails; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - fails = 0; - - TEST(fork()); - - if (TEST_RETURN == -1) { - /* fork failed */ - tst_brkm(TFAIL, cleanup, - "fork() failed with %d (%s)", - TEST_ERRNO, strerror(TEST_ERRNO)); - } else if (TEST_RETURN == 0) { - /* child */ - /* determine environment variables */ - child_environment(); - /* exit with known value */ - exit(KIDEXIT); - } else { - /* parent of successful fork */ - /* wait for the child to complete */ - wait_status = waitpid(TEST_RETURN, &kid_status, 0); - /* validate the child exit status */ - if (wait_status == TEST_RETURN) { - if (kid_status != KIDEXIT << 8) { - tst_brkm(TBROK, cleanup, - "fork(): Incorrect child status returned on wait(): %d", - kid_status); - fails++; - } - } else { - tst_brkm(TBROK, cleanup, - "fork(): wait() for child status failed with %d errno: %d : %s", - wait_status, errno, - strerror(errno)); - fails++; - } - - if (fails == 0) { - /* verification tests */ - parent_environment(); - } - } - - } - - cleanup(); - tst_exit(); -} +static struct tst_test test = { + .test_all = run, + .forks_child = 1, + .needs_checkpoints = 1, +}; diff --git a/testcases/kernel/syscalls/fork/fork05.c b/testcases/kernel/syscalls/fork/fork05.c index 9a99cff1..9aa12e16 100755 --- a/testcases/kernel/syscalls/fork/fork05.c +++ b/testcases/kernel/syscalls/fork/fork05.c @@ -1,150 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. - * Portions Copyright (c) 2000 Ulrich Drepper - * - * 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/ - * - * - * Linux Test Project - Silicon Graphics, Inc. - * TEST IDENTIFIER : fork05 - * EXECUTED BY : anyone - * TEST TITLE : Make sure LDT is propagated correctly - * TEST CASE TOTAL : 1 - * CPU TYPES : i386 - * AUTHORS : Ulrich Drepper - * Nate Straz - * - *On Friday, May 2, 2003 at 09:47:00AM MST, Ulrich Drepper wrote: - *>Robert Williamson wrote: - *> - *>> I'm getting a SIGSEGV with one of our tests, fork05.c, that apparently - *>> you wrote (attached below). The test passes on my 2.5.68 machine running - *>> SuSE 8.0 (glibc 2.2.5 and Linuxthreads), however it segmentation faults on - *>> RedHat 9 running 2.5.68. The test seems to "break" when it attempts to run - *>> the assembly code....could you take a look at it? - *> - *>There is no need to look at it, I know it cannot work anymore on recent - *>systems. Either change all uses of %gs to %fs or skip the entire patch - *>if %gs has a nonzero value. - *> - *>- -- - *>- --------------. ,-. 444 Castro Street - *>Ulrich Drepper \ ,-----------------' \ Mountain View, CA 94041 USA - *>Red Hat `--' drepper at redhat.com `--------------------------- - * - * - * - *On Sat, Aug 12, 2000 at 12:47:31PM -0700, Ulrich Drepper wrote: - *> Ever since the %gs handling was fixed in the 2.3.99 series the - *> appended test program worked. Now with 2.4.0-test6 it's not working - *> again. Looking briefly over the patch from test5 to test6 I haven't - *> seen an immediate candidate for the breakage. It could be missing - *> propagation of the LDT to the new process (and therefore an invalid - *> segment descriptor) or simply clearing %gs. - *> - *> Anyway, this is what you should see and what you get with test5: - *> - *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - *> a = 42 - *> %gs = 0x0007 - *> %gs = 0x0007 - *> a = 99 - *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - *> - *> This is what you get with test6: - *> - *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - *> a = 42 - *> %gs = 0x0007 - *> %gs = 0x0000 - *> - *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - *> - *> If somebody is actually creating a test suite for the kernel, please - *> add this program. It's mostly self-contained. The correct handling - *> of %gs is really important since glibc 2.2 will make heavy use of it. - *> - *> - -- - *> - ---------------. ,-. 1325 Chesapeake Terrace - *> Ulrich Drepper \ ,-------------------' \ Sunnyvale, CA 94089 USA - *> Red Hat `--' drepper at redhat.com `------------------------ - *> - *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * Author: Ulrich Drepper / Nate Straz , Red Hat + * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ -#include -#include -#include -#include -#include -#include "lapi/syscalls.h" -#include "test.h" +/*\ + * This test verifies that LDT is propagated correctly from parent process to + * the child process. + * + * On Friday, May 2, 2003 at 09:47:00AM MST, Ulrich Drepper wrote: + * + * Robert Williamson wrote: + * + * I'm getting a SIGSEGV with one of our tests, fork05.c, that apparently + * you wrote (attached below). The test passes on my 2.5.68 machine running + * SuSE 8.0 (glibc 2.2.5 and Linuxthreads), however it segmentation faults on + * RedHat 9 running 2.5.68. The test seems to "break" when it attempts to run + * the assembly code....could you take a look at it? + * + * There is no need to look at it, I know it cannot work anymore on recent + * systems. Either change all uses of %gs to %fs or skip the entire patch + * if %gs has a nonzero value. + * + * On Sat, Aug 12, 2000 at 12:47:31PM -0700, Ulrich Drepper wrote: + * + * Ever since the %gs handling was fixed in the 2.3.99 series the + * appended test program worked. Now with 2.4.0-test6 it's not working + * again. Looking briefly over the patch from test5 to test6 I haven't + * seen an immediate candidate for the breakage. It could be missing + * propagation of the LDT to the new process (and therefore an invalid + * segment descriptor) or simply clearing %gs. + * + * Anyway, this is what you should see and what you get with test5: + * + * a = 42 + * %gs = 0x0007 + * %gs = 0x0007 + * a = 99 + * + * This is what you get with test6: + * + * a = 42 + * %gs = 0x0007 + * %gs = 0x0000 + * + * + * If somebody is actually creating a test suite for the kernel, please + * add this program. It's mostly self-contained. The correct handling + * of %gs is really important since glibc 2.2 will make heavy use of it. + */ -char *TCID = "fork05"; +#include "tst_test.h" -static char *environ_list[] = { "TERM", "NoTSetzWq", "TESTPROG" }; +#if defined(__i386__) -#define NUMBER_OF_ENVIRON (sizeof(environ_list)/sizeof(char *)) -int TST_TOTAL = NUMBER_OF_ENVIRON; +#include "lapi/ldt.h" -#if defined(linux) && defined(__i386__) - -struct modify_ldt_ldt_s { - unsigned int entry_number; - unsigned long int base_addr; - unsigned int limit; - unsigned int seg_32bit:1; - unsigned int contents:2; - unsigned int read_exec_only:1; - unsigned int limit_in_pages:1; - unsigned int seg_not_present:1; - unsigned int useable:1; - unsigned int empty:25; -}; - -static int a = 42; - -static void modify_ldt(int func, struct modify_ldt_ldt_s *ptr, int bytecount) +static void run(void) { - tst_syscall(__NR_modify_ldt, func, ptr, bytecount); -} - -int main(void) -{ - struct modify_ldt_ldt_s ldt0; - int lo; + struct user_desc ldt0; + int base_addr = 42; + int status; pid_t pid; - int res; + int lo; ldt0.entry_number = 0; - ldt0.base_addr = (long)&a; + ldt0.base_addr = (long)&base_addr; ldt0.limit = 4; ldt0.seg_32bit = 1; ldt0.contents = 0; @@ -152,51 +74,44 @@ int main(void) ldt0.limit_in_pages = 0; ldt0.seg_not_present = 0; ldt0.useable = 1; - ldt0.empty = 0; - modify_ldt(1, &ldt0, sizeof(ldt0)); + SAFE_MODIFY_LDT(1, &ldt0, sizeof(ldt0)); asm volatile ("movw %w0, %%fs"::"q" (7)); - asm volatile ("movl %%fs:0, %0":"=r" (lo)); - tst_resm(TINFO, "a = %d", lo); + tst_res(TINFO, "a = %d", lo); asm volatile ("pushl %%fs; popl %0":"=q" (lo)); - tst_resm(TINFO, "%%fs = %#06hx", lo); + tst_res(TINFO, "%%fs = %#06hx", lo); asm volatile ("movl %0, %%fs:0"::"r" (99)); - pid = fork(); - - if (pid == 0) { + pid = SAFE_FORK(); + if (!pid) { asm volatile ("pushl %%fs; popl %0":"=q" (lo)); - tst_resm(TINFO, "%%fs = %#06hx", lo); + tst_res(TINFO, "%%fs = %#06hx", lo); asm volatile ("movl %%fs:0, %0":"=r" (lo)); - tst_resm(TINFO, "a = %d", lo); + tst_res(TINFO, "a = %d", lo); - if (lo != 99) - tst_resm(TFAIL, "Test failed"); - else - tst_resm(TPASS, "Test passed"); - exit(lo != 99); - } else { - waitpid(pid, &res, 0); + TST_EXP_EQ_LI(lo, 99); + + exit(0); } - return WIFSIGNALED(res); + SAFE_WAITPID(pid, &status, 0); + + if (WIFEXITED(status) && !WEXITSTATUS(status)) + tst_res(TPASS, "Child did exit with 0"); + else + tst_res(TFAIL, "Child %s", tst_strstatus(status)); } -#else /* if defined(linux) && defined(__i386__) */ +static struct tst_test test = { + .test_all = run, + .forks_child = 1, +}; -int main(void) -{ - tst_resm(TINFO, "%%fs test only for ix86"); - - /* - * should be successful on all non-ix86 platforms. - */ - tst_exit(); -} - -#endif /* if defined(linux) && defined(__i386__) */ +#else + TST_TEST_TCONF("Test only supports Intel 32 bits"); +#endif diff --git a/testcases/kernel/syscalls/fork/fork06.c b/testcases/kernel/syscalls/fork/fork06.c deleted file mode 100755 index 3bc11b14..00000000 --- a/testcases/kernel/syscalls/fork/fork06.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - * - * NAME - * fork06.c - * - * DESCRIPTION - * Test that a process can fork children a large number of - * times in succession - * - * ALGORITHM - * Attempt to fork a child that exits immediately - * Repeat it many times(1000), counting the number of successes and - * failures - * - * USAGE - * fork06 - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * None - */ - -#include -#include -#include -#include "test.h" - -char *TCID = "fork06"; -int TST_TOTAL = 1; - -static void setup(void); -static void cleanup(void); - -#define NUMFORKS 1000 - -int main(int ac, char **av) -{ - int i, pid, status, childpid, succeed, fail; - - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - succeed = 0; - fail = 0; - - for (i = 0; i < NUMFORKS; i++) { - pid = fork(); - if (pid == -1) { - fail++; - continue; - } - - if (pid == 0) - _exit(0); - - childpid = wait(&status); - if (pid != childpid) - tst_resm(TFAIL, "pid from wait %d", childpid); - succeed++; - } - - tst_resm(TINFO, "tries %d", i); - tst_resm(TINFO, "successes %d", succeed); - tst_resm(TINFO, "failures %d", fail); - - if ((wait(&status)) == -1) - tst_resm(TINFO, "There were no children to wait for"); - else - tst_resm(TINFO, "There were children left"); - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_sig(FORK, DEF_HANDLER, cleanup); - TEST_PAUSE; -} - -static void cleanup(void) -{ -} diff --git a/testcases/kernel/syscalls/fork/fork07.c b/testcases/kernel/syscalls/fork/fork07.c index 43b92c80..266f0ffb 100755 --- a/testcases/kernel/syscalls/fork/fork07.c +++ b/testcases/kernel/syscalls/fork/fork07.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Check that all children inherit parent's file descriptor. * * Parent opens a file and forks children. Each child reads a byte and checks diff --git a/testcases/kernel/syscalls/fork/fork08.c b/testcases/kernel/syscalls/fork/fork08.c index 34af476f..6a56cda7 100755 --- a/testcases/kernel/syscalls/fork/fork08.c +++ b/testcases/kernel/syscalls/fork/fork08.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that the parent's file descriptors will not be affected by being * closed in the child. */ diff --git a/testcases/kernel/syscalls/fork/fork10.c b/testcases/kernel/syscalls/fork/fork10.c index 815eee1f..6751a8f8 100755 --- a/testcases/kernel/syscalls/fork/fork10.c +++ b/testcases/kernel/syscalls/fork/fork10.c @@ -1,159 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - * - * - * NAME - * fork10.c - * - * DESCRIPTION - * Check inheritance of file descriptor by children, they - * should all be refering to the same file. - * - * ALGORITHM - * Child reads several chars and exits. - * Parent forks another child, have the child and parent attempt to use - * that location - * - * USAGE - * fork10 - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * None + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +/*\ + * This test verifies inheritance of file descriptors from parent to child + * process. We open a file from parent, then we check if file offset changes + * accordingly with file descriptor usage. + * + * [Algorithm] + * + * Test steps are the following: + * + * - create a file made in three parts -> | aa..a | bb..b | cc..c | + * - from parent, open the file + * - from child, move file offset after the first part + * - from parent, read second part and check if it's | bb..b | + * - from child, read third part and check if it's | cc..c | + * + * Test passes if we were able to read the correct file parts from parent and + * child. + */ -char *TCID = "fork10"; -int TST_TOTAL = 1; +#include +#include "tst_test.h" -static void setup(void); -static void cleanup(void); +#define FILENAME "file.txt" +#define DATASIZE 1024 -static char pidbuf[10]; -static char fnamebuf[40]; +static int fd = -1; -int main(int ac, char **av) +static void run(void) { - int status, pid, fildes; - char parchar[2]; - char chilchar[2]; + int status; + char buff[DATASIZE]; + char data[DATASIZE]; - int lc; + fd = SAFE_OPEN(FILENAME, 0); - fildes = -1; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - fildes = SAFE_CREAT(cleanup, fnamebuf, 0600); - write(fildes, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 27); - close(fildes); - - fildes = SAFE_OPEN(cleanup, fnamebuf, 0); - - pid = fork(); - if (pid == -1) - tst_brkm(TBROK, cleanup, "fork() #1 failed"); - - if (pid == 0) { /* child */ - tst_resm(TINFO, "fork child A"); - if (lseek(fildes, 10L, 0) == -1L) { - tst_resm(TFAIL, "bad lseek by child"); - exit(1); - } - exit(0); - } else { /* parent */ - wait(&status); - - /* parent starts second child */ - pid = fork(); - if (pid == -1) - tst_brkm(TBROK, cleanup, "fork() #2 failed"); - - if (pid == 0) { /* child */ - if (read(fildes, chilchar, 1) <= 0) { - tst_resm(TFAIL, "Child can't read " - "file"); - exit(1); - } else { - if (chilchar[0] != 'K') { - chilchar[1] = '\n'; - exit(1); - } else { - exit(0); - } - } - } else { /* parent */ - (void)wait(&status); - if (status >> 8 != 0) { - tst_resm(TFAIL, "Bad return from " - "second child"); - continue; - } - /* parent reads */ - if (read(fildes, parchar, 1) <= 0) { - tst_resm(TFAIL, "Parent cannot read " - "file"); - continue; - } else { - write(fildes, parchar, 1); - if (parchar[0] != 'L') { - parchar[1] = '\n'; - tst_resm(TFAIL, "Test failed"); - continue; - } - } - } - } - tst_resm(TPASS, "test 1 PASSED"); + if (!SAFE_FORK()) { + SAFE_LSEEK(fd, DATASIZE, SEEK_SET); + exit(0); } - close(fildes); - cleanup(); - tst_exit(); + SAFE_WAIT(&status); + + memset(buff, 'b', DATASIZE); + SAFE_READ(1, fd, data, DATASIZE); + + TST_EXP_EXPR(strncmp(buff, data, DATASIZE) == 0, + "read first part of data from parent process"); + + if (!SAFE_FORK()) { + memset(buff, 'c', DATASIZE); + SAFE_READ(1, fd, data, DATASIZE); + + TST_EXP_EXPR(strncmp(buff, data, DATASIZE) == 0, + "read second part of data from child process"); + + exit(0); + } + + SAFE_CLOSE(fd); } static void setup(void) { - tst_sig(FORK, DEF_HANDLER, cleanup); - umask(0); - TEST_PAUSE; - tst_tmpdir(); + char buff[DATASIZE]; - strcpy(fnamebuf, "fork10."); - sprintf(pidbuf, "%d", getpid()); - strcat(fnamebuf, pidbuf); + fd = SAFE_CREAT(FILENAME, 0600); + + memset(buff, 'a', DATASIZE); + SAFE_WRITE(SAFE_WRITE_ALL, fd, buff, DATASIZE); + + memset(buff, 'b', DATASIZE); + SAFE_WRITE(SAFE_WRITE_ALL, fd, buff, DATASIZE); + + memset(buff, 'c', DATASIZE); + SAFE_WRITE(SAFE_WRITE_ALL, fd, buff, DATASIZE); + + SAFE_CLOSE(fd); } static void cleanup(void) { - tst_rmdir(); + if (fd >= 0) + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .forks_child = 1, + .needs_tmpdir = 1, + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/fork/fork11.c b/testcases/kernel/syscalls/fork/fork11.c deleted file mode 100755 index 6afda3a3..00000000 --- a/testcases/kernel/syscalls/fork/fork11.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - * - * - * NAME - * fork11.c - * - * DESCRIPTION - * Test that parent gets a pid from each child when doing wait - * - * ALGORITHM - * Fork NUMFORKS children that do nothing. - * - * USAGE - * fork11 - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * None - */ - -#include -#include -#include -#include -#include "test.h" - -char *TCID = "fork11"; -int TST_TOTAL = 1; - -static void setup(void); -static void cleanup(void); - -#define NUMFORKS 100 - -int main(int ac, char **av) -{ - int i, pid, cpid, status; - int fail = 0; - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; i < NUMFORKS; i++) { - pid = fork(); - if (pid == 0) - exit(0); - - if (pid > 0) { /* parent */ - cpid = wait(&status); - if (cpid != pid) - fail++; - } else { - fail++; - break; - } - } - if (fail) - tst_resm(TFAIL, "fork failed %d times", fail); - else - tst_resm(TPASS, "fork test passed, %d processes", i); - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_sig(FORK, DEF_HANDLER, cleanup); - TEST_PAUSE; -} - -static void cleanup(void) -{ -} diff --git a/testcases/kernel/syscalls/fork/fork12.c b/testcases/kernel/syscalls/fork/fork12.c deleted file mode 100755 index 1c55c0c3..00000000 --- a/testcases/kernel/syscalls/fork/fork12.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - * - * - * NAME - * fork12.c - * - * DESCRIPTION - * Check that all children inherit parent's file descriptor - * - * ALGORITHM - * Parent forks processes until -1 is returned.$ - * - * USAGE - * fork12 - * ** CAUTION ** Can hang your machine, esp prior to 2.4.19 - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * 07/2002 Split from fork07 as a test case to exhaust available pids. - * - * RESTRICTIONS - * Should be run as root to avoid resource limits.$ - * Should not be run with other test programs because it tries to - * use all available pids. - */ - -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" - -char *TCID = "fork12"; -int TST_TOTAL = 1; - -static void setup(void); -static void cleanup(void); -static void fork12_sigs(int signum); - -int main(int ac, char **av) -{ - int forks, pid1, fork_errno, waitstatus; - int ret, status; - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - tst_resm(TINFO, "Forking as many kids as possible"); - forks = 0; - while ((pid1 = fork()) != -1) { - if (pid1 == 0) { /* child */ - /* - * Taunt the OOM killer so that it doesn't - * kill system processes - */ - SAFE_FILE_PRINTF(NULL, - "/proc/self/oom_score_adj", "500"); - pause(); - exit(0); - } - forks++; - ret = SAFE_WAITPID(cleanup, -1, &status, WNOHANG); - if (ret > 0) { - /* a child may be killed by OOM killer */ - if (WTERMSIG(status) == SIGKILL) - break; - tst_brkm(TBROK, cleanup, - "child exit with error code %d or signal %d", - WEXITSTATUS(status), WTERMSIG(status)); - } - } - fork_errno = errno; - - /* parent */ - tst_resm(TINFO, "Number of processes forked is %d", forks); - tst_resm(TPASS, "fork() eventually failed with %d: %s", - fork_errno, strerror(fork_errno)); - /* collect our kids */ - /* - * Introducing a sleep(3) to make sure all children are - * at pause() when SIGQUIT is sent to them - */ - sleep(3); - kill(0, SIGQUIT); - while (wait(&waitstatus) > 0) ; - - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_sig(FORK, fork12_sigs, cleanup); - TEST_PAUSE; -} - -static void cleanup(void) -{ - int waitstatus; - - /* collect our kids */ - kill(0, SIGQUIT); - while (wait(&waitstatus) > 0) ; -} - -static void fork12_sigs(int signum) -{ - if (signum == SIGQUIT) { - /* Children will continue, parent will ignore */ - } else { - tst_brkm(TBROK, cleanup, - "Unexpected signal %d received.", signum); - } -} diff --git a/testcases/kernel/syscalls/fork/fork13.c b/testcases/kernel/syscalls/fork/fork13.c index bbfbf5c3..415e9404 100755 --- a/testcases/kernel/syscalls/fork/fork13.c +++ b/testcases/kernel/syscalls/fork/fork13.c @@ -1,15 +1,14 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010 Red Hat, Inc. * Copyright (C) 2022 Cyril Hrubis */ /*\ - * [Description] - * * A race in pid generation that causes pids to be reused immediately * - * From the mainline commit 5fdee8c4a5e1800489ce61963208f8cc55e42ea1: + * From the mainline commit + * 5fdee8c4a5e1 ("pids: fix a race in pid generation that causes pids to be reused immediately") * * A program that repeatedly forks and waits is susceptible to having * the same pid repeated, especially when it competes with another @@ -17,21 +16,20 @@ * implementation. Furthermore, many shell scripts assume that pid * numbers will not be used for some length of time. * - * [Race Description] - * --------------------------------------------------------------------- - * A B + * [Race Description] :: * - * // pid == offset == n // pid == offset == n + 1 - * test_and_set_bit(offset, map->page) - * test_and_set_bit(offset, map->page); - * pid_ns->last_pid = pid; - * pid_ns->last_pid = pid; - * // pid == n + 1 is freed (wait()) + * A B * - * // Next fork()... - * last = pid_ns->last_pid; // == n - * pid = last + 1; - * --------------------------------------------------------------------- + * // pid == offset == n // pid == offset == n + 1 + * test_and_set_bit(offset, map->page) + * test_and_set_bit(offset, map->page); + * pid_ns->last_pid = pid; + * pid_ns->last_pid = pid; + * // pid == n + 1 is freed (wait()) + * + * // Next fork()... + * last = pid_ns->last_pid; // == n + * pid = last + 1; */ #include @@ -110,7 +108,7 @@ static void check(void) static struct tst_test test = { .needs_root = 1, .forks_child = 1, - .max_runtime = 600, + .runtime = 600, .test_all = check, .save_restore = (const struct tst_path_val[]) { {"/proc/sys/kernel/pid_max", PID_MAX_STR, TST_SR_TBROK}, diff --git a/testcases/kernel/syscalls/fork/fork14.c b/testcases/kernel/syscalls/fork/fork14.c index 93af2eba..ad3c0a9e 100755 --- a/testcases/kernel/syscalls/fork/fork14.c +++ b/testcases/kernel/syscalls/fork/fork14.c @@ -1,143 +1,122 @@ -/********************************************************************* +// SPDX-License-Identifier: GPL-2.0-only +/* * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test is a reproducer for kernel 3.5: + * 7edc8b0ac16c ("mm/fork: fix overflow in vma length when copying mmap on clone") * - * 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. - * - * This test is a reporducer for this patch: - * https://lkml.org/lkml/2012/4/24/328 - * Since vma length in dup_mmap is calculated and stored in a unsigned + * Since VMA length in dup_mmap() is calculated and stored in a unsigned * int, it will overflow when length of mmaped memory > 16 TB. When - * overflow occur, fork will incorrectly succeed. The patch above - * fixed it. - ********************************************************************/ + * overflow occurs, fork will incorrectly succeed. The patch above fixed it. + */ -#include +#include "tst_test.h" +#include #include -#include -#include -#include "test.h" -#include "safe_macros.h" -#include "lapi/abisize.h" -char *TCID = "fork14"; -int TST_TOTAL = 1; - -#define GB (1024 * 1024 * 1024L) - -/* set mmap threshold to 16TB */ #define LARGE (16 * 1024) #define EXTENT (16 * 1024 + 10) -static char **pointer_vec; +static char **memvec; -static void setup(void); -static void cleanup(void); -static int fork_test(void); - -int main(int ac, char **av) +static void run(void) { - int lc, reproduced; + int i, j, ret; + pid_t pid; + void *mem; + int prev_failed = 0; + int passed = 1; + int failures = 0; - tst_parse_opts(ac, av, NULL, NULL); -/* - * Tested on ppc64/x86_64/i386/s390x. And only 64bit has this issue. - * Since a 32bit program can't mmap so many memory. - */ -#ifdef TST_ABI32 - tst_brkm(TCONF, NULL, "This test is only for 64bit."); -#endif - setup(); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; + memset(memvec, 0, EXTENT); - reproduced = fork_test(); - if (reproduced == 0) - tst_resm(TPASS, "fork failed as expected."); + for (i = 0; i < EXTENT; i++) { + mem = mmap(NULL, 1 * TST_GB, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + 0, 0); + + if (mem == MAP_FAILED) { + failures++; + + tst_res(TINFO, "mmap() failed"); + + if (failures > 10) { + tst_brk(TCONF, "mmap() fails too many " + "times, so it's almost impossible to " + "get a vm_area_struct sized 16TB"); + } + + continue; + } + + memvec[i] = mem; + + pid = fork(); + + if (pid == -1) { + /* keep track of the failed fork() and verify that next one + * is failing as well. + */ + prev_failed = 1; + continue; + } + + if (!pid) + exit(0); + + ret = waitpid(pid, NULL, 0); + if (ret == -1 && errno != ECHILD) + tst_brk(TBROK | TERRNO, "waitpid() error"); + + if (prev_failed && i >= LARGE) { + passed = 0; + break; + } + + prev_failed = 0; + + tst_res(TDEBUG, "fork() passed at %d attempt", i); } - cleanup(); - tst_exit(); + + for (j = 0; j < i; j++) { + if (memvec[j]) + SAFE_MUNMAP(memvec[j], 1 * TST_GB); + } + + if (passed) + tst_res(TPASS, "fork() failed as expected"); + else + tst_res(TFAIL, "fork() succeeded incorrectly"); } static void setup(void) { - tst_sig(FORK, DEF_HANDLER, cleanup); - TEST_PAUSE; - - pointer_vec = SAFE_MALLOC(cleanup, EXTENT * sizeof(char *)); + memvec = SAFE_MALLOC(EXTENT * sizeof(char *)); } static void cleanup(void) { - free(pointer_vec); -} - -static int fork_test(void) -{ - int i, j, prev_failed = 0, fails = 0, cnt = 0; - int reproduced = 0; - void *addr; - - for (i = 0; i < EXTENT; i++) { - addr = mmap(NULL, 1 * GB, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); - if (addr == MAP_FAILED) { - pointer_vec[i] = NULL; - fails++; - /* - * EXTENT is "16*1024+10", if fails count exceeds 10, - * we are almost impossible to get an vm_area_struct - * sized 16TB - */ - if (fails == 11) { - tst_brkm(TCONF, cleanup, "mmap() fails too many" - "times, so we are almost impossible to" - " get an vm_area_struct sized 16TB."); - } - } else { - pointer_vec[i] = addr; - } - cnt++; - - switch (tst_fork()) { - case -1: - prev_failed = 1; - break; - case 0: - exit(0); - default: - SAFE_WAITPID(cleanup, -1, NULL, 0); - - if (prev_failed > 0 && i >= LARGE) { - tst_resm(TFAIL, "Fork succeeds incorrectly"); - reproduced = 1; - goto clear_memory_map; - } - } + for (long i = 0; i < EXTENT; i++) { + if (memvec && memvec[i]) + SAFE_MUNMAP(memvec[i], 1 * TST_GB); } -clear_memory_map: - for (j = 0; j < cnt; j++) { - if (pointer_vec[j]) - SAFE_MUNMAP(cleanup, pointer_vec[j], 1 * GB); - } - - return reproduced; + if (memvec) + free(memvec); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .needs_abi_bits = 64, + .tags = (const struct tst_tag[]) { + {"linux-git", "7edc8b0ac16c"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/fork/fork_procs.c b/testcases/kernel/syscalls/fork/fork_procs.c new file mode 100644 index 00000000..9e084ed6 --- /dev/null +++ b/testcases/kernel/syscalls/fork/fork_procs.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test spawns multiple processes using fork() and it checks if wait() + * returns the right PID once they end up. + */ + +#include +#include "tst_test.h" + +static char *str_numforks; +static int numforks = 1000; + +static void run(void) +{ + pid_t pid; + int status; + int counter = 0; + + tst_res(TINFO, "Forking %d processes", numforks); + + for (int i = 0; i < numforks; i++) { + pid = SAFE_FORK(); + if (!pid) + exit(0); + + if (SAFE_WAIT(&status) == pid) + counter++; + } + + TST_EXP_EXPR(numforks == counter, + "%d processes ended successfully", + counter); +} + +static void setup(void) +{ + if (tst_parse_int(str_numforks, &numforks, 1, INT_MAX)) + tst_brk(TBROK, "wrong number of forks '%s'", str_numforks); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .options = (struct tst_option[]) { + { "n:", &str_numforks, "Number of processes to spawn" }, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/fpathconf/fpathconf01.c b/testcases/kernel/syscalls/fpathconf/fpathconf01.c index d38db401..d3654dd3 100755 --- a/testcases/kernel/syscalls/fpathconf/fpathconf01.c +++ b/testcases/kernel/syscalls/fpathconf/fpathconf01.c @@ -1,54 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. - * Author: William Roske - * Co-pilot: Dave Fenner - * - * 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/ - * + * Copyright (c) Linux Test Project, 2003-2024 */ -/* - * Testcase to test the basic functionality of fpathconf(2) system call. +/*\ + * Check the basic functionality of the fpathconf(2) system call. */ -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" -static void setup(void); -static void cleanup(void); - -static struct pathconf_args { +static struct tcase { char *name; int value; } test_cases[] = { @@ -63,67 +25,30 @@ static struct pathconf_args { {"_PC_NO_TRUNC", _PC_NO_TRUNC}, }; -char *TCID = "fpathconf01"; -int TST_TOTAL = ARRAY_SIZE(test_cases); - static int fd; -int main(int ac, char **av) +static void verify_fpathconf(unsigned int n) { - int lc; - int i; + struct tcase *tc = &test_cases[n]; - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - for (i = 0; i < TST_TOTAL; i++) { - errno = 0; - - TEST(fpathconf(fd, test_cases[i].value)); - - if (TEST_RETURN == -1) { - if (TEST_ERRNO == 0) { - tst_resm(TINFO, - "fpathconf has NO limit for " - "%s", test_cases[i].name); - } else { - tst_resm(TFAIL | TTERRNO, - "fpathconf(fd, %s) failed", - test_cases[i].name); - } - } else { - tst_resm(TPASS, - "fpathconf(fd, %s) returned %ld", - test_cases[i].name, TEST_RETURN); - } - } - } - - cleanup(); - - tst_exit(); + TST_EXP_POSITIVE(fpathconf(fd, tc->value)); } static void setup(void) { - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - fd = SAFE_OPEN(cleanup, "fpafile01", O_RDWR | O_CREAT, 0700); + fd = SAFE_OPEN("fpafile01", O_RDWR | O_CREAT, 0700); } static void cleanup(void) { if (fd > 0) - SAFE_CLOSE(NULL, fd); - - tst_rmdir(); + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .needs_tmpdir = 1, + .test = verify_fpathconf, + .tcnt = ARRAY_SIZE(test_cases), + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/fremovexattr/fremovexattr01.c b/testcases/kernel/syscalls/fremovexattr/fremovexattr01.c index 907d210d..aca2fce7 100755 --- a/testcases/kernel/syscalls/fremovexattr/fremovexattr01.c +++ b/testcases/kernel/syscalls/fremovexattr/fremovexattr01.c @@ -83,6 +83,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 12, .setup = setup, .test_all = verify_fremovexattr, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/fremovexattr/fremovexattr02.c b/testcases/kernel/syscalls/fremovexattr/fremovexattr02.c index eb106a8f..78af8be3 100755 --- a/testcases/kernel/syscalls/fremovexattr/fremovexattr02.c +++ b/testcases/kernel/syscalls/fremovexattr/fremovexattr02.c @@ -105,6 +105,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 10, .setup = setup, .test = verify_fremovexattr, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/fsconfig/fsconfig01.c b/testcases/kernel/syscalls/fsconfig/fsconfig01.c index a585daa6..678d2181 100755 --- a/testcases/kernel/syscalls/fsconfig/fsconfig01.c +++ b/testcases/kernel/syscalls/fsconfig/fsconfig01.c @@ -82,6 +82,7 @@ static void run(void) } static struct tst_test test = { + .timeout = 10, .test_all = run, .setup = fsopen_supported_by_kernel, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/fsconfig/fsconfig03.c b/testcases/kernel/syscalls/fsconfig/fsconfig03.c index 0ba5355d..43fd4c4a 100644 --- a/testcases/kernel/syscalls/fsconfig/fsconfig03.c +++ b/testcases/kernel/syscalls/fsconfig/fsconfig03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test for CVE-2022-0185. * * References links: @@ -53,12 +51,14 @@ static void run(void) if (!TST_RET) { tst_res(TFAIL, "fsconfig() passed unexpectedly"); + break; } else if (TST_RET != -1) { tst_brk(TBROK | TTERRNO, "Invalid fsconfig() return value %ld", TST_RET); } else if (TST_ERR != EINVAL) { tst_res(TFAIL | TTERRNO, "fsconfig() failed with unexpected error"); + break; } } @@ -80,6 +80,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 9, .test_all = run, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/fsetxattr/fsetxattr01.c b/testcases/kernel/syscalls/fsetxattr/fsetxattr01.c index d799e477..73e1fcfb 100755 --- a/testcases/kernel/syscalls/fsetxattr/fsetxattr01.c +++ b/testcases/kernel/syscalls/fsetxattr/fsetxattr01.c @@ -140,7 +140,7 @@ static void verify_fsetxattr(unsigned int i) { /* some tests might require existing keys for each iteration */ if (tc[i].keyneeded) { - SAFE_FSETXATTR(fd, tc[i].key, tc[i].value, tc[i].size, + SAFE_FSETXATTR(fd, tc[i].key, *tc[i].value, tc[i].size, XATTR_CREATE); } @@ -214,6 +214,7 @@ static void setup(void) } static struct tst_test test = { + .timeout = 10, .setup = setup, .test = verify_fsetxattr, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/fsetxattr/fsetxattr02.c b/testcases/kernel/syscalls/fsetxattr/fsetxattr02.c index 0336c964..18490a86 100755 --- a/testcases/kernel/syscalls/fsetxattr/fsetxattr02.c +++ b/testcases/kernel/syscalls/fsetxattr/fsetxattr02.c @@ -1,27 +1,31 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Linaro Limited. All rights reserved. + * Copyright (c) Linux Test Project, 2018-2023 * Author: Rafael David Tinoco */ +/*\ + * Verify basic fsetxattr(2) syscall functionality: + * + * - Set attribute to a regular file, fsetxattr(2) should succeed. + * - Set attribute to a directory, fsetxattr(2) should succeed. + * - Set attribute to a symlink which points to the regular file, + * fsetxattr(2) should return -1 and set errno to EEXIST. + * - Set attribute to a FIFO, fsetxattr(2) should return -1 and set + * errno to EPERM. + * - Set attribute to a char special file, fsetxattr(2) should + * return -1 and set errno to EPERM. + * - Set attribute to a block special file, fsetxattr(2) should + * return -1 and set errno to EPERM. + * - Set attribute to a UNIX domain socket, fsetxattr(2) should + * return -1 and set errno to EPERM. + */ + /* * In the user.* namespace, only regular files and directories can * have extended attributes. Otherwise fsetxattr(2) will return -1 * and set errno to EPERM. - * - * There are 7 test cases: - * 1. Set attribute to a regular file, fsetxattr(2) should succeed - * 2. Set attribute to a directory, fsetxattr(2) should succeed - * 3. Set attribute to a symlink which points to the regular file, - * fsetxattr(2) should return -1 and set errno to EEXIST - * 4. Set attribute to a FIFO, fsetxattr(2) should return -1 and set - * errno to EPERM - * 5. Set attribute to a char special file, fsetxattr(2) should - * return -1 and set errno to EPERM - * 6. Set attribute to a block special file, fsetxattr(2) should - * return -1 and set errno to EPERM - * 7. Set attribute to a UNIX domain socket, fsetxattr(2) should - * return -1 and set errno to EPERM */ #include "config.h" diff --git a/testcases/kernel/syscalls/fsmount/fsmount01.c b/testcases/kernel/syscalls/fsmount/fsmount01.c index 5f755863..9444a2d7 100755 --- a/testcases/kernel/syscalls/fsmount/fsmount01.c +++ b/testcases/kernel/syscalls/fsmount/fsmount01.c @@ -8,6 +8,7 @@ #include "tst_test.h" #include "lapi/fsmount.h" +#include "tst_safe_stdio.h" #define MNTPOINT "mntpoint" @@ -81,6 +82,32 @@ static void run(unsigned int n) return; } + switch (tc->attrs) { + case MOUNT_ATTR_RDONLY: + TST_EXP_VAL(tst_is_mounted_ro(MNTPOINT), 1); + break; + case MOUNT_ATTR_NOSUID: + TST_EXP_VAL(tst_mount_has_opt(MNTPOINT, "nosuid"), 1); + break; + case MOUNT_ATTR_NODEV: + TST_EXP_VAL(tst_mount_has_opt(MNTPOINT, "nodev"), 1); + break; + case MOUNT_ATTR_NOEXEC: + TST_EXP_VAL(tst_mount_has_opt(MNTPOINT, "noexec"), 1); + break; + case MOUNT_ATTR_RELATIME: + TST_EXP_VAL(tst_mount_has_opt(MNTPOINT, "relatime"), 1); + break; + case MOUNT_ATTR_NOATIME: + TST_EXP_VAL(tst_mount_has_opt(MNTPOINT, "noatime"), 1); + break; + case MOUNT_ATTR_NODIRATIME: + TST_EXP_VAL(tst_mount_has_opt(MNTPOINT, "nodiratime"), 1); + break; + default: + break; + } + if (tst_is_mounted_at_tmpdir(MNTPOINT)) { SAFE_UMOUNT(MNTPOINT); tst_res(TPASS, "%s: fsmount() passed", tc->name); @@ -88,6 +115,7 @@ static void run(unsigned int n) } static struct tst_test test = { + .timeout = 10, .tcnt = ARRAY_SIZE(tcases), .test = run, .setup = fsopen_supported_by_kernel, diff --git a/testcases/kernel/syscalls/fsmount/fsmount02.c b/testcases/kernel/syscalls/fsmount/fsmount02.c index a4f42dc1..55f0e2f2 100755 --- a/testcases/kernel/syscalls/fsmount/fsmount02.c +++ b/testcases/kernel/syscalls/fsmount/fsmount02.c @@ -68,6 +68,7 @@ static void run(unsigned int n) } static struct tst_test test = { + .timeout = 9, .tcnt = ARRAY_SIZE(tcases), .test = run, .setup = setup, diff --git a/testcases/kernel/syscalls/fsopen/fsopen01.c b/testcases/kernel/syscalls/fsopen/fsopen01.c index c2c719c9..9dd87b99 100755 --- a/testcases/kernel/syscalls/fsopen/fsopen01.c +++ b/testcases/kernel/syscalls/fsopen/fsopen01.c @@ -69,6 +69,7 @@ out: } static struct tst_test test = { + .timeout = 9, .tcnt = ARRAY_SIZE(tcases), .test = run, .setup = fsopen_supported_by_kernel, diff --git a/testcases/kernel/syscalls/fspick/fspick01.c b/testcases/kernel/syscalls/fspick/fspick01.c index d3309a91..dab2bd75 100755 --- a/testcases/kernel/syscalls/fspick/fspick01.c +++ b/testcases/kernel/syscalls/fspick/fspick01.c @@ -6,6 +6,7 @@ */ #include "tst_test.h" #include "lapi/fsmount.h" +#include "tst_safe_stdio.h" #define MNTPOINT "mntpoint" #define TCASE_ENTRY(_flags) {.name = "Flag " #_flags, .flags = _flags} @@ -25,6 +26,8 @@ static void run(unsigned int n) struct tcase *tc = &tcases[n]; int fspick_fd; + TST_EXP_VAL(tst_is_mounted_rw(MNTPOINT), 1); + TEST(fspick_fd = fspick(AT_FDCWD, MNTPOINT, tc->flags)); if (fspick_fd == -1) { tst_res(TFAIL | TTERRNO, "fspick() failed"); @@ -49,13 +52,28 @@ static void run(unsigned int n) goto out; } - tst_res(TPASS, "%s: fspick() passed", tc->name); + TST_EXP_VAL(tst_is_mounted_ro(MNTPOINT), 1); + TEST(fsconfig(fspick_fd, FSCONFIG_SET_FLAG, "rw", NULL, 0)); + if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "fsconfig(FSCONFIG_SET_FLAG) failed"); + goto out; + } + + TEST(fsconfig(fspick_fd, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0)); + if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "fsconfig(FSCONFIG_CMD_RECONFIGURE) failed"); + goto out; + } + + TST_EXP_VAL(tst_is_mounted_rw(MNTPOINT), 1); + tst_res(TPASS, "%s: fspick() passed", tc->name); out: SAFE_CLOSE(fspick_fd); } static struct tst_test test = { + .timeout = 9, .tcnt = ARRAY_SIZE(tcases), .test = run, .setup = fsopen_supported_by_kernel, diff --git a/testcases/kernel/syscalls/fspick/fspick02.c b/testcases/kernel/syscalls/fspick/fspick02.c index f9a3697c..89bdd2cc 100755 --- a/testcases/kernel/syscalls/fspick/fspick02.c +++ b/testcases/kernel/syscalls/fspick/fspick02.c @@ -43,6 +43,7 @@ static void run(unsigned int n) } static struct tst_test test = { + .timeout = 9, .tcnt = ARRAY_SIZE(tcases), .test = run, .setup = fsopen_supported_by_kernel, diff --git a/testcases/kernel/syscalls/fstat/fstat02.c b/testcases/kernel/syscalls/fstat/fstat02.c index 27683175..68cbe82f 100755 --- a/testcases/kernel/syscalls/fstat/fstat02.c +++ b/testcases/kernel/syscalls/fstat/fstat02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests if fstat() returns correctly and reports correct file information * using the stat structure. */ diff --git a/testcases/kernel/syscalls/fstatfs/fstatfs01.c b/testcases/kernel/syscalls/fstatfs/fstatfs01.c index 6b14fd0d..d9523889 100755 --- a/testcases/kernel/syscalls/fstatfs/fstatfs01.c +++ b/testcases/kernel/syscalls/fstatfs/fstatfs01.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2022 SUSE LLC Avinesh Kumar */ /*\ - * [Description] - * * Verify that fstatfs() syscall executes successfully for all * available filesystems. */ @@ -56,6 +54,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 9, .setup = setup, .cleanup = cleanup, .tcnt = ARRAY_SIZE(tcases), diff --git a/testcases/kernel/syscalls/fstatfs/fstatfs02.c b/testcases/kernel/syscalls/fstatfs/fstatfs02.c index f801c9f5..dcb9589e 100755 --- a/testcases/kernel/syscalls/fstatfs/fstatfs02.c +++ b/testcases/kernel/syscalls/fstatfs/fstatfs02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Testcase to check if fstatfs() sets errno correctly. */ diff --git a/testcases/kernel/syscalls/fsync/fsync01.c b/testcases/kernel/syscalls/fsync/fsync01.c index 072245fc..7ae5296c 100755 --- a/testcases/kernel/syscalls/fsync/fsync01.c +++ b/testcases/kernel/syscalls/fsync/fsync01.c @@ -44,6 +44,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 10, .cleanup = cleanup, .setup = setup, .test_all = verify_fsync, diff --git a/testcases/kernel/syscalls/fsync/fsync02.c b/testcases/kernel/syscalls/fsync/fsync02.c index c9de5c72..3293415f 100755 --- a/testcases/kernel/syscalls/fsync/fsync02.c +++ b/testcases/kernel/syscalls/fsync/fsync02.c @@ -115,5 +115,5 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .needs_tmpdir = 1, - .max_runtime = 300, + .timeout = 300, }; diff --git a/testcases/kernel/syscalls/fsync/fsync03.c b/testcases/kernel/syscalls/fsync/fsync03.c index d32c4a3e..3eb47046 100755 --- a/testcases/kernel/syscalls/fsync/fsync03.c +++ b/testcases/kernel/syscalls/fsync/fsync03.c @@ -4,14 +4,14 @@ * Copyright (c) 2019 SUSE LLC */ -/* - * Test Description: - * Testcase to check that fsync(2) sets errno correctly. - * 1. Call fsync() on a pipe(fd), and expect EINVAL. - * 2. Call fsync() on a socket(fd), and expect EINVAL. - * 3. Call fsync() on a closed fd, and test for EBADF. - * 4. Call fsync() on an invalid fd, and test for EBADF. - * 5. Call fsync() on a fifo(fd), and expect EINVAL. +/*\ + * Verify that, fsync(2) returns -1 and sets errno to + * + * - EINVAL if calling fsync() on a pipe(fd). + * - EINVAL if calling fsync() on a socket(fd). + * - EBADF if calling fsync() on a closed fd. + * - EBADF if calling fsync() on an invalid fd. + * - EINVAL if calling fsync() on a fifo(fd). */ #include diff --git a/testcases/kernel/syscalls/fsync/fsync04.c b/testcases/kernel/syscalls/fsync/fsync04.c index 9aa1584c..f7553ff5 100755 --- a/testcases/kernel/syscalls/fsync/fsync04.c +++ b/testcases/kernel/syscalls/fsync/fsync04.c @@ -53,6 +53,7 @@ static void verify_fsync(void) } static struct tst_test test = { + .timeout = 17, .needs_root = 1, .mount_device = 1, .all_filesystems = 1, diff --git a/testcases/kernel/syscalls/ftruncate/ftruncate01.c b/testcases/kernel/syscalls/ftruncate/ftruncate01.c index 5ffdd051..13f915be 100755 --- a/testcases/kernel/syscalls/ftruncate/ftruncate01.c +++ b/testcases/kernel/syscalls/ftruncate/ftruncate01.c @@ -4,11 +4,11 @@ * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. * Author: Wayne Boyer */ -/* - * Test Description: - * Verify that, ftruncate() succeeds to truncate a file to a certain length, - * if the file previously is smaller than the truncated size, ftruncate() - * shall increase the size of the file. + +/*\ + * Verify that, ftruncate() succeeds to truncate a file to a certain length, + * if the file previously is smaller than the truncated size, ftruncate() + * shall increase the size of the file. */ #include diff --git a/testcases/kernel/syscalls/ftruncate/ftruncate03.c b/testcases/kernel/syscalls/ftruncate/ftruncate03.c index f2fa96dc..368186e2 100755 --- a/testcases/kernel/syscalls/ftruncate/ftruncate03.c +++ b/testcases/kernel/syscalls/ftruncate/ftruncate03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that ftruncate(2) system call returns appropriate error number: * * 1. EINVAL -- the file is a socket diff --git a/testcases/kernel/syscalls/futex/futex2test.h b/testcases/kernel/syscalls/futex/futex2test.h index ce97f47c..a3cd0ef5 100644 --- a/testcases/kernel/syscalls/futex/futex2test.h +++ b/testcases/kernel/syscalls/futex/futex2test.h @@ -12,6 +12,14 @@ #include #include "lapi/syscalls.h" #include "futextest.h" +#include "lapi/abisize.h" + +#ifdef TST_ABI32 +struct timespec64 { + int64_t tv_sec; + int64_t tv_nsec; +}; +#endif /** * futex_waitv - Wait at multiple futexes, wake on any @@ -24,7 +32,16 @@ static inline int futex_waitv(volatile struct futex_waitv *waiters, unsigned long nr_waiters, unsigned long flags, struct timespec *timo, clockid_t clockid) { +#ifdef TST_ABI32 + struct timespec64 timo64 = {0}; + + timo64.tv_sec = timo->tv_sec; + timo64.tv_nsec = timo->tv_nsec; + return tst_syscall(__NR_futex_waitv, waiters, nr_waiters, flags, &timo64, clockid); +#else return tst_syscall(__NR_futex_waitv, waiters, nr_waiters, flags, timo, clockid); + +#endif } #endif /* _FUTEX2TEST_H */ diff --git a/testcases/kernel/syscalls/futex/futex_cmp_requeue01.c b/testcases/kernel/syscalls/futex/futex_cmp_requeue01.c index 87270446..51b5c6a8 100755 --- a/testcases/kernel/syscalls/futex/futex_cmp_requeue01.c +++ b/testcases/kernel/syscalls/futex/futex_cmp_requeue01.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Xiao Yang + */ + +/*\ + * Verify the basic functionality of futex(FUTEX_CMP_REQUEUE). * - * Description: - * Testcase to check the basic functionality of futex(FUTEX_CMP_REQUEUE). * futex(FUTEX_CMP_REQUEUE) can wake up the number of waiters specified * by val argument and then requeue the number of waiters limited by val2 - * argument(i.e. move some remaining waiters from uaddr to uaddr2 address). + * argument (i.e. move some remaining waiters from uaddr to uaddr2 address). */ #include @@ -20,8 +22,8 @@ struct shared_data { futex_t futexes[2]; - int spurious; - int test_done; + tst_atomic_t spurious; + tst_atomic_t test_done; }; static struct shared_data *sd; diff --git a/testcases/kernel/syscalls/futex/futex_waitv01.c b/testcases/kernel/syscalls/futex/futex_waitv01.c index 17b96738..bbea6648 100644 --- a/testcases/kernel/syscalls/futex/futex_waitv01.c +++ b/testcases/kernel/syscalls/futex/futex_waitv01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test verifies EINVAL for futex_waitv syscall. */ diff --git a/testcases/kernel/syscalls/futex/futex_waitv02.c b/testcases/kernel/syscalls/futex/futex_waitv02.c index ccea5eb5..6b210d38 100644 --- a/testcases/kernel/syscalls/futex/futex_waitv02.c +++ b/testcases/kernel/syscalls/futex/futex_waitv02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test verifies futex_waitv syscall using private data. */ diff --git a/testcases/kernel/syscalls/futex/futex_waitv03.c b/testcases/kernel/syscalls/futex/futex_waitv03.c index c674f52d..1d367ed0 100644 --- a/testcases/kernel/syscalls/futex/futex_waitv03.c +++ b/testcases/kernel/syscalls/futex/futex_waitv03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test verifies futex_waitv syscall using shared data. */ diff --git a/testcases/kernel/syscalls/get_mempolicy/Makefile b/testcases/kernel/syscalls/get_mempolicy/Makefile index a108d820..517838f4 100755 --- a/testcases/kernel/syscalls/get_mempolicy/Makefile +++ b/testcases/kernel/syscalls/get_mempolicy/Makefile @@ -4,7 +4,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpnuma +LTPLIBS = numa include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/get_mempolicy/get_mempolicy01.c b/testcases/kernel/syscalls/get_mempolicy/get_mempolicy01.c index 23f62091..3c854d10 100755 --- a/testcases/kernel/syscalls/get_mempolicy/get_mempolicy01.c +++ b/testcases/kernel/syscalls/get_mempolicy/get_mempolicy01.c @@ -12,8 +12,6 @@ */ /*\ - * [Description] - * * Verify that get_mempolicy() returns a proper return value and errno for various cases. */ diff --git a/testcases/kernel/syscalls/get_mempolicy/get_mempolicy02.c b/testcases/kernel/syscalls/get_mempolicy/get_mempolicy02.c index 4a855596..79ff5d94 100755 --- a/testcases/kernel/syscalls/get_mempolicy/get_mempolicy02.c +++ b/testcases/kernel/syscalls/get_mempolicy/get_mempolicy02.c @@ -12,8 +12,6 @@ */ /*\ - * [Description] - * * Verify that get_mempolicy() returns a proper return errno for failure cases. */ diff --git a/testcases/kernel/syscalls/getcontext/getcontext01.c b/testcases/kernel/syscalls/getcontext/getcontext01.c index 5896c380..0d8955e3 100755 --- a/testcases/kernel/syscalls/getcontext/getcontext01.c +++ b/testcases/kernel/syscalls/getcontext/getcontext01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for getcontext(). * * Calls a getcontext() then jumps back with a setcontext(). diff --git a/testcases/kernel/syscalls/getcpu/.gitignore b/testcases/kernel/syscalls/getcpu/.gitignore index 31fec5d3..cd3022bb 100755 --- a/testcases/kernel/syscalls/getcpu/.gitignore +++ b/testcases/kernel/syscalls/getcpu/.gitignore @@ -1 +1,2 @@ /getcpu01 +/getcpu02 diff --git a/testcases/kernel/syscalls/getcpu/getcpu01.c b/testcases/kernel/syscalls/getcpu/getcpu01.c index 9842c899..19d8c9d8 100755 --- a/testcases/kernel/syscalls/getcpu/getcpu01.c +++ b/testcases/kernel/syscalls/getcpu/getcpu01.c @@ -1,11 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright © International Business Machines Corp., 2007, 2008 + * Copyright (c) Linux Test Project, 2009-2024 * - * Test Description: - * The test process is affined to a CPU. It then calls getcpu and - * checks that the CPU and node (if supported) match the expected - * values. + */ + +/*\ + * The test process is affined to a CPU. It then calls getcpu and + * checks that the CPU and node (if supported) match the expected + * values. */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/getcpu/getcpu02.c b/testcases/kernel/syscalls/getcpu/getcpu02.c new file mode 100644 index 00000000..954151c0 --- /dev/null +++ b/testcases/kernel/syscalls/getcpu/getcpu02.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Copyright (c) Linux Test Project, 2024 + * Author: Ma Xinjian + */ + +/*\ + * Verify that getcpu(2) fails with EFAULT if cpu_id or node_id points outside + * the calling process address space. + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/sched.h" + +static unsigned int cpu_id, node_id; + +static struct tcase { + unsigned int *cpu_id; + unsigned int *node_id; + char *desc; +} tcases[] = { + {NULL, &node_id, "cpu_id"}, + {&cpu_id, NULL, "node_id"}, +}; + +static void check_getcpu(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + int status; + pid_t pid; + + tst_res(TINFO, "Test %s outside process address space", tc->desc); + + if (!tc->cpu_id) + tc->cpu_id = tst_get_bad_addr(NULL); + + if (!tc->node_id) + tc->node_id = tst_get_bad_addr(NULL); + + pid = SAFE_FORK(); + if (!pid) { + TST_EXP_FAIL(getcpu(tc->cpu_id, tc->node_id), EFAULT); + + exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "getcpu() caused SIGSEGV"); + return; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + return; + + tst_res(TFAIL, "child %s", tst_strstatus(status)); +} + +static struct tst_test test = { + .test = check_getcpu, + .tcnt = ARRAY_SIZE(tcases), + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/getcwd/getcwd01.c b/testcases/kernel/syscalls/getcwd/getcwd01.c index 65d82787..218bf4ef 100755 --- a/testcases/kernel/syscalls/getcwd/getcwd01.c +++ b/testcases/kernel/syscalls/getcwd/getcwd01.c @@ -14,17 +14,17 @@ * * Expected Result: * 1) getcwd(2) should return NULL and set errno to EFAULT. - * 2) getcwd(2) should return NULL and set errno to ENOMEM. - * 3) getcwd(2) should return NULL and set errno to EINVAL. + * 2) getcwd(2) should return NULL and set errno to EFAULT. + * 3) getcwd(2) should return NULL and set errno to ERANGE. * 4) getcwd(2) should return NULL and set errno to ERANGE. * 5) getcwd(2) should return NULL and set errno to ERANGE. - * */ #include #include #include #include "tst_test.h" +#include "lapi/syscalls.h" static char buffer[5]; @@ -34,32 +34,18 @@ static struct t_case { int exp_err; } tcases[] = { {(void *)-1, PATH_MAX, EFAULT}, - {NULL, (size_t)-1, ENOMEM}, - {buffer, 0, EINVAL}, + {NULL, (size_t)-1, EFAULT}, + {buffer, 0, ERANGE}, {buffer, 1, ERANGE}, {NULL, 1, ERANGE} }; + static void verify_getcwd(unsigned int n) { struct t_case *tc = &tcases[n]; - char *res; - errno = 0; - res = getcwd(tc->buf, tc->size); - TST_ERR = errno; - if (res) { - tst_res(TFAIL, "getcwd() succeeded unexpectedly"); - return; - } - - if (TST_ERR != tc->exp_err) { - tst_res(TFAIL | TTERRNO, "getcwd() failed unexpectedly, expected %s", - tst_strerrno(tc->exp_err)); - return; - } - - tst_res(TPASS | TTERRNO, "getcwd() failed as expected"); + TST_EXP_FAIL2(tst_syscall(__NR_getcwd, tc->buf, tc->size), tc->exp_err); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/getcwd/getcwd02.c b/testcases/kernel/syscalls/getcwd/getcwd02.c index cb111a69..4950c2d2 100755 --- a/testcases/kernel/syscalls/getcwd/getcwd02.c +++ b/testcases/kernel/syscalls/getcwd/getcwd02.c @@ -3,12 +3,12 @@ * Copyright (c) International Business Machines Corp., 2001 */ -/* - * DESCRIPTION +/*\ * Testcase to check the basic functionality of the getcwd(2) system call. - * 1) getcwd(2) works fine if buf and size are valid. - * 2) getcwd(2) works fine if buf points to NULL and size is set to 0. - * 3) getcwd(2) works fine if buf points to NULL and size is greater than strlen(path). + * + * 1. getcwd(2) works fine if buf and size are valid. + * 2. getcwd(2) works fine if buf points to NULL and size is set to 0. + * 3. getcwd(2) works fine if buf points to NULL and size is greater than strlen(path). */ #include diff --git a/testcases/kernel/syscalls/getcwd/getcwd03.c b/testcases/kernel/syscalls/getcwd/getcwd03.c index 97f4f3a3..4ea4e3ad 100755 --- a/testcases/kernel/syscalls/getcwd/getcwd03.c +++ b/testcases/kernel/syscalls/getcwd/getcwd03.c @@ -3,18 +3,18 @@ * Copyright (c) International Business Machines Corp., 2001 */ -/* - * DESCRIPTION +/*\ * Testcase to check the basic functionality of the getcwd(2) * system call on a symbolic link. * - * ALGORITHM - * 1) create a directory, and create a symbolic link to it at the + * [Algorithm] + * + * 1. create a directory, and create a symbolic link to it at the * same directory level. - * 2) get the working directory of a directory, and its pathname. - * 3) get the working directory of a symbolic link, and its pathname, + * 2. get the working directory of a directory, and its pathname. + * 3. get the working directory of a symbolic link, and its pathname, * and its readlink info. - * 4) compare the working directories and link information. + * 4. compare the working directories and link information. */ #define _GNU_SOURCE 1 diff --git a/testcases/kernel/syscalls/getdents/getdents.h b/testcases/kernel/syscalls/getdents/getdents.h index 560df412..02c3bc50 100755 --- a/testcases/kernel/syscalls/getdents/getdents.h +++ b/testcases/kernel/syscalls/getdents/getdents.h @@ -64,9 +64,9 @@ tst_dirp_size(void) { switch (tst_variant) { case 0: - return sizeof(struct linux_dirent); + return sizeof(struct linux_dirent) + NAME_MAX; case 1: - return sizeof(struct linux_dirent64); + return sizeof(struct linux_dirent64) + NAME_MAX; #if HAVE_GETDENTS case 2: return sizeof(struct dirent); diff --git a/testcases/kernel/syscalls/getdents/getdents01.c b/testcases/kernel/syscalls/getdents/getdents01.c index e5e4689e..6a88e305 100755 --- a/testcases/kernel/syscalls/getdents/getdents01.c +++ b/testcases/kernel/syscalls/getdents/getdents01.c @@ -7,8 +7,6 @@ */ /*\ - * - * [Description] * * Basic getdents() test that checks if directory listing is correct and * complete. @@ -22,6 +20,9 @@ #include +#define MNTPOINT "mntpoint" +#define WORKDIR MNTPOINT "/workdir" + static void reset_flags(void); static void check_flags(void); static void set_flag(const char *name); @@ -61,7 +62,7 @@ static void run(void) { int rval; - fd = SAFE_OPEN(".", O_RDONLY|O_DIRECTORY); + fd = SAFE_OPEN(WORKDIR, O_RDONLY|O_DIRECTORY); rval = tst_getdents(fd, dirp, BUFSIZE); @@ -147,31 +148,37 @@ static void set_flag(const char *name) static void setup(void) { size_t i; + char path[255]; getdents_info(); - if (!tst_variant) { - for (i = 0; i < ARRAY_SIZE(testcases); i++) { - if (!testcases[i].create) - continue; + /* + * Work in a subdirectory because some filesystems add special files + * or directories to their root. + */ + SAFE_MKDIR(WORKDIR, 0777); - switch (testcases[i].type) { - case ENTRY_DIR: - SAFE_MKDIR(testcases[i].name, 0777); - break; - case ENTRY_FILE: - SAFE_FILE_PRINTF(testcases[i].name, " "); - break; - case ENTRY_SYMLINK: - SAFE_SYMLINK("nonexistent", testcases[i].name); - break; - } + for (i = 0; i < ARRAY_SIZE(testcases); i++) { + if (!testcases[i].create) + continue; + + sprintf(path, "%s/%s", WORKDIR, testcases[i].name); + + switch (testcases[i].type) { + case ENTRY_DIR: + SAFE_MKDIR(path, 0777); + break; + case ENTRY_FILE: + SAFE_FILE_PRINTF(path, " "); + break; + case ENTRY_SYMLINK: + SAFE_SYMLINK("nonexistent", path); + break; } } } static struct tst_test test = { - .needs_tmpdir = 1, .test_all = run, .setup = setup, .bufs = (struct tst_buffers []) { @@ -179,4 +186,13 @@ static struct tst_test test = { {}, }, .test_variants = TEST_VARIANTS, + .needs_root = 1, + .all_filesystems = 1, + .mount_device = 1, + .mntpoint = MNTPOINT, + .skip_filesystems = (const char *const[]) { + "vfat", + "exfat", + NULL + } }; diff --git a/testcases/kernel/syscalls/getdents/getdents02.c b/testcases/kernel/syscalls/getdents/getdents02.c index ade1c947..98254e12 100755 --- a/testcases/kernel/syscalls/getdents/getdents02.c +++ b/testcases/kernel/syscalls/getdents/getdents02.c @@ -7,14 +7,13 @@ */ /*\ - * [Description] - * * Verify that: * * - getdents() fails with EBADF if file descriptor fd is invalid * - getdents() fails with EINVAL if result buffer is too small * - getdents() fails with ENOTDIR if file descriptor does not refer to a directory * - getdents() fails with ENOENT if directory was unlinked() + * - getdents() fails with EFAULT if argument points outside the calling process's address space */ #define _GNU_SOURCE @@ -23,17 +22,17 @@ #include "tst_test.h" #include "getdents.h" -#define DIR_MODE (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP| \ - S_IXGRP|S_IROTH|S_IXOTH) -#define TEST_DIR "test_dir" - -char *TCID = "getdents02"; +#define DIR_MODE 0755 +#define MNTPOINT "mntpoint" +#define TEST_DIR MNTPOINT "/test_dir" +#define TEST_FILE MNTPOINT "/test" static char *dirp; static size_t size; static char dirp1_arr[1]; static char *dirp1 = dirp1_arr; +static char *dirp_bad; static size_t size1 = 1; static int fd_inv = -5; @@ -51,6 +50,7 @@ static struct tcase { { &fd, &dirp1, &size1, EINVAL }, { &fd_file, &dirp, &size, ENOTDIR }, { &fd_unlinked, &dirp, &size, ENOENT }, + { &fd, &dirp_bad, &size, EFAULT }, }; static void setup(void) @@ -60,8 +60,10 @@ static void setup(void) size = tst_dirp_size(); dirp = tst_alloc(size); - fd = SAFE_OPEN(".", O_RDONLY); - fd_file = SAFE_OPEN("test", O_CREAT | O_RDWR, 0644); + fd = SAFE_OPEN(MNTPOINT, O_RDONLY); + fd_file = SAFE_OPEN(TEST_FILE, O_CREAT | O_RDWR, 0644); + + dirp_bad = tst_get_bad_addr(NULL); SAFE_MKDIR(TEST_DIR, DIR_MODE); fd_unlinked = SAFE_OPEN(TEST_DIR, O_DIRECTORY); @@ -72,26 +74,18 @@ static void run(unsigned int i) { struct tcase *tc = tcases + i; - TEST(tst_getdents(*tc->fd, *tc->dirp, *tc->size)); - - if (TST_RET != -1) { - tst_res(TFAIL, "getdents() returned %ld", TST_RET); - return; - } - - if (TST_ERR == tc->exp_errno) { - tst_res(TPASS | TTERRNO, "getdents failed as expected"); - } else if (errno == ENOSYS) { - tst_res(TCONF, "syscall not implemented"); - } else { - tst_res(TFAIL | TTERRNO, "getdents failed unexpectedly"); - } + TST_EXP_FAIL2(tst_getdents(*tc->fd, *tc->dirp, *tc->size), + tc->exp_errno, "fd=%i dirp=%p size=%zu", + *tc->fd, *tc->dirp, *tc->size); } static struct tst_test test = { - .needs_tmpdir = 1, .test = run, .setup = setup, .tcnt = ARRAY_SIZE(tcases), .test_variants = TEST_VARIANTS, + .needs_root = 1, + .all_filesystems = 1, + .mount_device = 1, + .mntpoint = MNTPOINT }; diff --git a/testcases/kernel/syscalls/getdomainname/getdomainname01.c b/testcases/kernel/syscalls/getdomainname/getdomainname01.c index 17a9392e..34c55264 100755 --- a/testcases/kernel/syscalls/getdomainname/getdomainname01.c +++ b/testcases/kernel/syscalls/getdomainname/getdomainname01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic test for getdomainname(2) * * This is a Phase I test for the getdomainname(2) system call. diff --git a/testcases/kernel/syscalls/getdtablesize/.gitignore b/testcases/kernel/syscalls/getdtablesize/.gitignore deleted file mode 100755 index 67a71b5e..00000000 --- a/testcases/kernel/syscalls/getdtablesize/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/getdtablesize01 diff --git a/testcases/kernel/syscalls/getdtablesize/getdtablesize01.c b/testcases/kernel/syscalls/getdtablesize/getdtablesize01.c deleted file mode 100755 index d25cac26..00000000 --- a/testcases/kernel/syscalls/getdtablesize/getdtablesize01.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2005 - * Copyright (c) Wipro Technologies Ltd, 2005. 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. - * - * 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. - * - */ -/********************************************************** - * - * TEST IDENTIFIER : getdtablesize01 - * - * EXECUTED BY : root / superuser - * - * TEST TITLE : Basic tests for getdtablesize01(2) - * - * TEST CASE TOTAL : 1 - * - * AUTHOR : Prashant P Yendigeri - * - * Robbie Williamson - * - * - * DESCRIPTION - * This is a Phase I test for the getdtablesize01(2) system call. - * It is intended to provide a limited exposure of the system call. - * - **********************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" - -void setup(); -void cleanup(); - -char *TCID = "getdtablesize01"; -int TST_TOTAL = 1; - -int main(void) -{ - int table_size, fd = 0, count = 0; - int max_val_opfiles; - struct rlimit rlp; - - setup(); - table_size = getdtablesize(); - getrlimit(RLIMIT_NOFILE, &rlp); - max_val_opfiles = (rlim_t) rlp.rlim_cur; - - tst_resm(TINFO, - "Maximum number of files a process can have opened is %d", - table_size); - tst_resm(TINFO, - "Checking with the value returned by getrlimit...RLIMIT_NOFILE"); - - if (table_size == max_val_opfiles) - tst_resm(TPASS, "got correct dtablesize, value is %d", - max_val_opfiles); - else { - tst_resm(TFAIL, "got incorrect table size, value is %d", - max_val_opfiles); - cleanup(); - } - - tst_resm(TINFO, - "Checking Max num of files that can be opened by a process.Should be: RLIMIT_NOFILE - 1"); - for (;;) { - fd = open("/etc/hosts", O_RDONLY); - - if (fd == -1) - break; - count = fd; - -#ifdef DEBUG - printf("Opened file num %d\n", fd); -#endif - } - -//Now the max files opened should be RLIMIT_NOFILE - 1 , why ? read getdtablesize man page - - if (count > 0) - close(count); - if (count == (max_val_opfiles - 1)) - tst_resm(TPASS, "%d = %d", count, (max_val_opfiles - 1)); - else if (fd < 0 && errno == ENFILE) - tst_brkm(TCONF, cleanup, "Reached maximum number of open files for the system"); - else - tst_resm(TFAIL, "%d != %d", count, (max_val_opfiles - 1)); - - cleanup(); - tst_exit(); -} - -void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - -void cleanup(void) -{ -} diff --git a/testcases/kernel/syscalls/getegid/getegid01.c b/testcases/kernel/syscalls/getegid/getegid01.c index 271fbb6b..42999196 100755 --- a/testcases/kernel/syscalls/getegid/getegid01.c +++ b/testcases/kernel/syscalls/getegid/getegid01.c @@ -1,87 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * 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/ - * + * William Roske, Dave Fenner + * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ -/* - * AUTHOR : William Roske - * CO-PILOT : Dave Fenner +/*\ + * This test checks if getegid() returns the effective group id. */ -#include -#include -#include -#include +#include "tst_test.h" +#include "compat_tst_16.h" -#include "test.h" -#include "compat_16.h" - -static void setup(); -static void cleanup(); - -TCID_DEFINE(getegid01); -int TST_TOTAL = 1; - -int main(int ac, char **av) +static void run(void) { - int lc; + gid_t gid, st_egid; - tst_parse_opts(ac, av, NULL, NULL); + SAFE_FILE_LINES_SCANF("/proc/self/status", "Gid: %*d %d", &st_egid); + gid = getegid(); - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - TEST(GETEGID(cleanup)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "getegid failed"); - continue; /* next loop for MTKERNEL */ - } - - tst_resm(TPASS, "getegid returned %ld", TEST_RETURN); - } - - cleanup(); - tst_exit(); + if (GID_SIZE_CHECK(st_egid)) + TST_EXP_EQ_LI(gid, st_egid); + else + tst_res(TPASS, "getegid() passed"); } -static void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - TEST_PAUSE; -} - -static void cleanup(void) -{ -} +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/syscalls/getegid/getegid02.c b/testcases/kernel/syscalls/getegid/getegid02.c index 60f09501..32635e83 100755 --- a/testcases/kernel/syscalls/getegid/getegid02.c +++ b/testcases/kernel/syscalls/getegid/getegid02.c @@ -1,90 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2001 - * Ported by Wayne Boyer - * - * 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 + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * William Roske, Dave Fenner + * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ -/* - * Testcase to check the basic functionality of getegid(). - * - * For functionality test the return value from getegid() is compared to passwd - * entry. +/*\ + * This test checks if getegid() returns the same effective group given by + * passwd entry via getpwuid(). */ #include -#include -#include "test.h" -#include "compat_16.h" +#include "tst_test.h" +#include "compat_tst_16.h" -static void cleanup(void); -static void setup(void); - -TCID_DEFINE(getegid02); -int TST_TOTAL = 1; - -int main(int ac, char **av) +static void run(void) { - int lc; uid_t euid; + gid_t egid; struct passwd *pwent; - tst_parse_opts(ac, av, NULL, NULL); + UID16_CHECK((euid = geteuid()), "geteuid"); - setup(); + pwent = getpwuid(euid); + if (!pwent) + tst_brk(TBROK | TERRNO, "getpwuid() error"); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; + GID16_CHECK((egid = getegid()), "getegid"); - TEST(GETEGID(cleanup)); - - if (TEST_RETURN < 0) { - tst_brkm(TBROK, cleanup, "This should never happen"); - } - - euid = geteuid(); - pwent = getpwuid(euid); - - if (pwent == NULL) - tst_brkm(TBROK, cleanup, "geteuid() returned " - "unexpected value %d", euid); - - GID16_CHECK(pwent->pw_gid, getegid, cleanup); - - if (pwent->pw_gid != TEST_RETURN) { - tst_resm(TFAIL, "getegid() return value" - " %ld unexpected - expected %d", - TEST_RETURN, pwent->pw_gid); - } else { - tst_resm(TPASS, - "effective group id %ld " - "is correct", TEST_RETURN); - } - } - - cleanup(); - tst_exit(); + TST_EXP_EQ_LI(pwent->pw_gid, egid); } -static void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - TEST_PAUSE; -} - -static void cleanup(void) -{ -} +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/syscalls/geteuid/geteuid01.c b/testcases/kernel/syscalls/geteuid/geteuid01.c index 66fb8936..1fe38f6c 100755 --- a/testcases/kernel/syscalls/geteuid/geteuid01.c +++ b/testcases/kernel/syscalls/geteuid/geteuid01.c @@ -8,8 +8,6 @@ */ /*\ - *[Description] - * * Check the basic functionality of the geteuid() system call. */ diff --git a/testcases/kernel/syscalls/geteuid/geteuid02.c b/testcases/kernel/syscalls/geteuid/geteuid02.c index eb2272bf..09cb816f 100755 --- a/testcases/kernel/syscalls/geteuid/geteuid02.c +++ b/testcases/kernel/syscalls/geteuid/geteuid02.c @@ -6,8 +6,6 @@ */ /*\ - *[Description] - * * Check that geteuid() return value matches value from /proc/self/status. */ diff --git a/testcases/kernel/syscalls/getgid/getgid01.c b/testcases/kernel/syscalls/getgid/getgid01.c index dec0fca4..2481b149 100755 --- a/testcases/kernel/syscalls/getgid/getgid01.c +++ b/testcases/kernel/syscalls/getgid/getgid01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * AUTHOR : William Roske @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Call getgid() and expects that the gid returned correctly. */ diff --git a/testcases/kernel/syscalls/getgid/getgid03.c b/testcases/kernel/syscalls/getgid/getgid03.c index eb302ad3..80b0a0fc 100755 --- a/testcases/kernel/syscalls/getgid/getgid03.c +++ b/testcases/kernel/syscalls/getgid/getgid03.c @@ -5,10 +5,10 @@ */ /*\ - * [Description] * Testcase to check the basic functionality of getgid(). * * [Algorithm] + * * For functionality test the return value from getgid() is compared to passwd * entry. */ diff --git a/testcases/kernel/syscalls/getgroups/Makefile b/testcases/kernel/syscalls/getgroups/Makefile index b2bb1e00..a319acf8 100755 --- a/testcases/kernel/syscalls/getgroups/Makefile +++ b/testcases/kernel/syscalls/getgroups/Makefile @@ -3,6 +3,9 @@ top_srcdir ?= ../../../.. +# Remove after rewriting all tests to the new API. +USE_LEGACY_COMPAT_16_H := 1 + include $(top_srcdir)/include/mk/testcases.mk include $(abs_srcdir)/../utils/compat_16.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/getgroups/getgroups01.c b/testcases/kernel/syscalls/getgroups/getgroups01.c index cfddeb40..c253e487 100755 --- a/testcases/kernel/syscalls/getgroups/getgroups01.c +++ b/testcases/kernel/syscalls/getgroups/getgroups01.c @@ -54,6 +54,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" static void setup(void); diff --git a/testcases/kernel/syscalls/getgroups/getgroups03.c b/testcases/kernel/syscalls/getgroups/getgroups03.c index 5ba20ef8..fc94f0b8 100755 --- a/testcases/kernel/syscalls/getgroups/getgroups03.c +++ b/testcases/kernel/syscalls/getgroups/getgroups03.c @@ -40,6 +40,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" #define TESTUSER "root" diff --git a/testcases/kernel/syscalls/gethostbyname_r/gethostbyname_r01.c b/testcases/kernel/syscalls/gethostbyname_r/gethostbyname_r01.c index 53261ea4..e4c0cb71 100755 --- a/testcases/kernel/syscalls/gethostbyname_r/gethostbyname_r01.c +++ b/testcases/kernel/syscalls/gethostbyname_r/gethostbyname_r01.c @@ -1,36 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Fujitsu Ltd. * Author: Zeng Linggang - * - * 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. + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -/* - * This is a test for glibc bug: +/*\ + * Test for GHOST: glibc vulnerability (CVE-2015-0235). + * * https://www.qualys.com/research/security-advisories/GHOST-CVE-2015-0235.txt */ -#include -#include -#include -#include -#include -#include "test.h" +#include "tst_test.h" #define CANARY "in_the_coal_mine" -static void setup(void); -static void check_vulnerable(void); - -static struct { +static struct +{ char buffer[1024]; char canary[sizeof(CANARY)]; } temp = { @@ -38,31 +24,6 @@ static struct { CANARY, }; -char *TCID = "gethostbyname_r01"; -int TST_TOTAL = 1; - -int main(int ac, char **av) -{ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - check_vulnerable(); - } - - tst_exit(); -} - -static void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, NULL); - TEST_PAUSE; -} - static void check_vulnerable(void) { struct hostent resbuf; @@ -78,22 +39,24 @@ static void check_vulnerable(void) * sizeof(*h_addr_ptrs) - 1; */ len = sizeof(temp.buffer) - 16 - 2 * sizeof(char *) - 1; + memset(name, '0', len); name[len] = '\0'; - retval = gethostbyname_r(name, &resbuf, temp.buffer, sizeof(temp.buffer), &result, &herrno); - if (strcmp(temp.canary, CANARY) != 0) { - tst_resm(TFAIL, "vulnerable"); - return; - } - - if (retval == ERANGE) { - tst_resm(TPASS, "not vulnerable"); - return; - } - - tst_resm(TFAIL, "gethostbyname_r() returned %s, expected ERANGE", - tst_strerrno(retval)); + /* has canary been overwritten? */ + if (strcmp(temp.canary, CANARY) != 0) + tst_res(TFAIL, "GHOST CVE-2015-0235 vulnerable"); + else + TST_EXP_EQ_LI(retval, ERANGE); } + +static struct tst_test test = { + .test_all = check_vulnerable, + .tags = (const struct tst_tag[]) { + {"glibc-git", "d5dd6189d506"}, + {"CVE", "CVE-2015-0235"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/gethostid/gethostid01.c b/testcases/kernel/syscalls/gethostid/gethostid01.c index c176e518..e5cf201a 100755 --- a/testcases/kernel/syscalls/gethostid/gethostid01.c +++ b/testcases/kernel/syscalls/gethostid/gethostid01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2021 Xie Ziyao @@ -19,8 +19,6 @@ */ /*\ - * [Description] - * * Test the basic functionality of the sethostid() and gethostid() system call. */ diff --git a/testcases/kernel/syscalls/gethostname/.gitignore b/testcases/kernel/syscalls/gethostname/.gitignore index d09d5d28..d6e4cffc 100755 --- a/testcases/kernel/syscalls/gethostname/.gitignore +++ b/testcases/kernel/syscalls/gethostname/.gitignore @@ -1 +1,2 @@ /gethostname01 +/gethostname02 diff --git a/testcases/kernel/syscalls/gethostname/gethostname01.c b/testcases/kernel/syscalls/gethostname/gethostname01.c index f2276a38..6ef8a634 100755 --- a/testcases/kernel/syscalls/gethostname/gethostname01.c +++ b/testcases/kernel/syscalls/gethostname/gethostname01.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2023 SUSE LLC Ioannis Bonatakis */ /*\ - * [Description] - * * Test is checking that gethostname() succeeds. */ diff --git a/testcases/kernel/syscalls/gethostname/gethostname02.c b/testcases/kernel/syscalls/gethostname/gethostname02.c new file mode 100644 index 00000000..c56fcb80 --- /dev/null +++ b/testcases/kernel/syscalls/gethostname/gethostname02.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Author: Yang Xu + */ + +/*\ + * Verify that gethostname(2) fails with + * + * - ENAMETOOLONG when len is smaller than the actual size + */ + +#include "tst_test.h" + +static void verify_gethostname(void) +{ + char hostname[HOST_NAME_MAX + 1]; + int real_length; + + SAFE_GETHOSTNAME(hostname, sizeof(hostname)); + real_length = strlen(hostname); + + TST_EXP_FAIL(gethostname(hostname, real_length - 1), ENAMETOOLONG, + "len is smaller than the actual size"); +} + +static struct tst_test test = { + .test_all = verify_gethostname, +}; diff --git a/testcases/kernel/syscalls/getitimer/getitimer01.c b/testcases/kernel/syscalls/getitimer/getitimer01.c index 6b0cfba1..a32a2860 100755 --- a/testcases/kernel/syscalls/getitimer/getitimer01.c +++ b/testcases/kernel/syscalls/getitimer/getitimer01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that a correct call to getitimer() succeeds. */ diff --git a/testcases/kernel/syscalls/getitimer/getitimer02.c b/testcases/kernel/syscalls/getitimer/getitimer02.c index 8ddbaec4..8bf8453e 100755 --- a/testcases/kernel/syscalls/getitimer/getitimer02.c +++ b/testcases/kernel/syscalls/getitimer/getitimer02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that getitimer() call fails: * * 1. EFAULT with invalid itimerval pointer diff --git a/testcases/kernel/syscalls/getpagesize/getpagesize01.c b/testcases/kernel/syscalls/getpagesize/getpagesize01.c index 2ba8ba70..3e810978 100755 --- a/testcases/kernel/syscalls/getpagesize/getpagesize01.c +++ b/testcases/kernel/syscalls/getpagesize/getpagesize01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) International Business Machines Corp., 2005 * Robbie Williamson @@ -10,8 +10,6 @@ */ /*\ - * [Description] - * * Verify that getpagesize(2) returns the number of bytes in a * memory page as expected. */ diff --git a/testcases/kernel/syscalls/getpeername/getpeername01.c b/testcases/kernel/syscalls/getpeername/getpeername01.c index 817cd38a..945b83d2 100755 --- a/testcases/kernel/syscalls/getpeername/getpeername01.c +++ b/testcases/kernel/syscalls/getpeername/getpeername01.c @@ -1,178 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * 07/2001 Ported by Wayne Boyer - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -/* - * Verify that getpeername() returns the proper errno for various failure cases +/*\ + * Verify that getpeername() returns the proper errno for various failure cases: + * + * - EBADF on invalid address. + * - ENOTSOCK on socket opened on /dev/null. + * - ENOTCONN on socket not connected. + * - EINVAL on negative addrlen. + * - EFAULT on invalid addr/addrlen pointers. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" static struct sockaddr_in server_addr; static struct sockaddr_in fsin1; static socklen_t sinlen; static socklen_t invalid_sinlen = -1; static int sv[2]; +static int sockfd = -1; -static void setup(void); -static void setup2(int); -static void setup3(int); -static void setup4(int); -static void cleanup(void); -static void cleanup2(int); -static void cleanup4(int); +static void setup_fd_file(void) +{ + sockfd = SAFE_OPEN("/dev/null", O_WRONLY, 0666); +} -struct test_case_t { - int sockfd; +static void setup_fd_stream(void) +{ + sockfd = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); + SAFE_BIND(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); +} + +static void cleanup_fd(void) +{ + if (sockfd) + SAFE_CLOSE(sockfd); +} + +static void setup_pair(void) +{ + SAFE_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sv); + sockfd = sv[0]; +} + +static void cleanup_pair(void) +{ + if (sv[0]) + SAFE_CLOSE(sv[0]); + + if (sv[1]) + SAFE_CLOSE(sv[1]); +} + +static struct test_case { struct sockaddr *sockaddr; socklen_t *addrlen; - int expretval; int experrno; - void (*setup) (int); - void (*cleanup) (int); - char *name; + void (*setup)(void); + void (*cleanup)(void); } test_cases[] = { - {-1, (struct sockaddr *)&fsin1, &sinlen, -1, EBADF, NULL, NULL, - "EBADF"}, - {-1, (struct sockaddr *)&fsin1, &sinlen, -1, ENOTSOCK, setup2, cleanup2, - "ENOTSOCK"}, - {-1, (struct sockaddr *)&fsin1, &sinlen, -1, ENOTCONN, setup3, cleanup2, - "ENOTCONN"}, - {-1, (struct sockaddr *)&fsin1, &invalid_sinlen, -1, EINVAL, setup4, - cleanup4, "EINVAL"}, -#ifndef UCLINUX - {-1, (struct sockaddr *)-1, &sinlen, -1, EFAULT, setup4, cleanup4, - "EFAULT"}, - {-1, (struct sockaddr *)&fsin1, NULL, -1, EFAULT, setup4, - cleanup4, "EFAULT"}, - {-1, (struct sockaddr *)&fsin1, (socklen_t *)1, -1, EFAULT, setup4, - cleanup4, "EFAULT"}, -#endif + {.addrlen = &sinlen, .experrno = EBADF}, + {.addrlen = &sinlen, .experrno = ENOTSOCK, .setup = setup_fd_file, + .cleanup = cleanup_fd }, + {.addrlen = &sinlen, .experrno = ENOTCONN, .setup = setup_fd_stream, + .cleanup = cleanup_fd }, + {.addrlen = &invalid_sinlen, .experrno = EINVAL, .setup = setup_pair, + .cleanup = cleanup_pair}, + {.sockaddr = (struct sockaddr *) -1, .addrlen = &sinlen, .experrno = EFAULT, + .setup = setup_pair, .cleanup = cleanup_pair}, + {.experrno = EFAULT, .setup = setup_pair, .cleanup = cleanup_pair}, + {.addrlen = (socklen_t *) 1, .experrno = EFAULT, .setup = setup_pair, + .cleanup = cleanup_pair }, }; -char *TCID = "getpeername01"; -int TST_TOTAL = ARRAY_SIZE(test_cases); - -int main(int argc, char *argv[]) +static void verify_getpeername(unsigned int nr) { - int lc; - int i; + struct test_case *tc = &test_cases[nr]; - tst_parse_opts(argc, argv, NULL, NULL); + if (tc->setup != NULL) + tc->setup(); - setup(); + if (!tc->sockaddr) + tc->sockaddr = (struct sockaddr *)&fsin1; - for (lc = 0; TEST_LOOPING(lc); ++lc) { + TST_EXP_FAIL(getpeername(sockfd, tc->sockaddr, tc->addrlen), tc->experrno); - tst_count = 0; - - for (i = 0; i < TST_TOTAL; ++i) { - - if (test_cases[i].setup != NULL) - test_cases[i].setup(i); - - TEST(getpeername(test_cases[i].sockfd, - test_cases[i].sockaddr, - test_cases[i].addrlen)); - - if (TEST_RETURN == test_cases[i].expretval && - TEST_ERRNO == test_cases[i].experrno) { - tst_resm(TPASS, - "test getpeername() %s successful", - test_cases[i].name); - } else { - tst_resm(TFAIL, - "test getpeername() %s failed; " - "returned %ld (expected %d), errno %d " - "(expected %d)", test_cases[i].name, - TEST_RETURN, test_cases[i].expretval, - TEST_ERRNO, test_cases[i].experrno); - } - - if (test_cases[i].cleanup != NULL) - test_cases[i].cleanup(i); - } - } - - cleanup(); - - tst_exit(); + if (tc->cleanup != NULL) + tc->cleanup(); } static void setup(void) { - TEST_PAUSE; - server_addr.sin_family = AF_INET; server_addr.sin_port = 0; server_addr.sin_addr.s_addr = INADDR_ANY; - sinlen = sizeof(fsin1); } -static void cleanup(void) -{ -} - -static void setup2(int i) -{ - test_cases[i].sockfd = SAFE_OPEN(cleanup, "/dev/null", O_WRONLY, 0666); -} - -static void setup3(int i) -{ - test_cases[i].sockfd = socket(PF_INET, SOCK_STREAM, 0); - if (test_cases[i].sockfd < 0) { - tst_brkm(TBROK | TERRNO, cleanup, - "socket setup failed for getpeername test %d", i); - } - SAFE_BIND(cleanup, test_cases[i].sockfd, - (struct sockaddr *)&server_addr, sizeof(server_addr)); -} - -static void setup4(int i) -{ - if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) { - tst_brkm(TBROK | TERRNO, cleanup, - "socketpair failed for getpeername test %d", i); - } - test_cases[i].sockfd = sv[0]; -} - -static void cleanup2(int i) -{ - SAFE_CLOSE(cleanup, test_cases[i].sockfd); -} - -static void cleanup4(int i) -{ - SAFE_CLOSE(cleanup, sv[0]); - SAFE_CLOSE(cleanup, sv[1]); -} +static struct tst_test test = { + .setup = setup, + .test = verify_getpeername, + .tcnt = ARRAY_SIZE(test_cases), +}; diff --git a/testcases/kernel/syscalls/getpgid/getpgid01.c b/testcases/kernel/syscalls/getpgid/getpgid01.c index 479fe5dc..169ea3d6 100755 --- a/testcases/kernel/syscalls/getpgid/getpgid01.c +++ b/testcases/kernel/syscalls/getpgid/getpgid01.c @@ -6,13 +6,19 @@ */ /*\ - * [Description] - * * Verify the basic functionality of getpgid(2) syscall. */ #include "tst_test.h" +static int get_init_pgid(void) +{ + int pgid; + + SAFE_FILE_SCANF("/proc/1/stat", "%*d %*s %*c %*d %d", &pgid); + return pgid; +} + static void run(void) { pid_t pid_1, child_pid, pgid; @@ -37,7 +43,7 @@ static void run(void) TST_EXP_EQ_LI(TST_RET, pgid); TST_EXP_PID(getpgid(1)); - TST_EXP_EQ_LI(TST_RET, 1); + TST_EXP_EQ_LI(TST_RET, get_init_pgid()); } tst_reap_children(); diff --git a/testcases/kernel/syscalls/getpgid/getpgid02.c b/testcases/kernel/syscalls/getpgid/getpgid02.c index 30d0129b..2836160d 100755 --- a/testcases/kernel/syscalls/getpgid/getpgid02.c +++ b/testcases/kernel/syscalls/getpgid/getpgid02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that getpgid(2) fails with errno ESRCH when * pid does not match any process. */ diff --git a/testcases/kernel/syscalls/getpgrp/getpgrp01.c b/testcases/kernel/syscalls/getpgrp/getpgrp01.c index a9473666..06a6e0ae 100755 --- a/testcases/kernel/syscalls/getpgrp/getpgrp01.c +++ b/testcases/kernel/syscalls/getpgrp/getpgrp01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * AUTHOR: William Roske, CO-PILOT: Dave Fenner @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that getpgrp(2) syscall executes successfully. */ diff --git a/testcases/kernel/syscalls/getpid/getpid01.c b/testcases/kernel/syscalls/getpid/getpid01.c index ec18b67d..d9ba0953 100755 --- a/testcases/kernel/syscalls/getpid/getpid01.c +++ b/testcases/kernel/syscalls/getpid/getpid01.c @@ -4,28 +4,31 @@ */ /*\ - * [Description] - * - * Verify that getpid() system call returns process ID in range 2 ... PID_MAX + * Verify that getpid() system call returns process ID in range <2, PID_MAX>. */ #include #include "tst_test.h" +static pid_t pid_max; + +static void setup(void) +{ + SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%d\n", &pid_max); +} + static void verify_getpid(void) { - pid_t pid_max, pid; + pid_t pid; int i; - SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%d\n", &pid_max); - for (i = 0; i < 100; i++) { pid = SAFE_FORK(); if (pid == 0) { pid = getpid(); /* pid should not be 1 or out of maximum */ - if (1 < pid && pid <= pid_max) + if (pid > 1 && pid <= pid_max) tst_res(TPASS, "getpid() returns %d", pid); else tst_res(TFAIL, @@ -38,6 +41,8 @@ static void verify_getpid(void) } static struct tst_test test = { + .timeout = 1, + .setup = setup, .forks_child = 1, .test_all = verify_getpid, }; diff --git a/testcases/kernel/syscalls/getpid/getpid02.c b/testcases/kernel/syscalls/getpid/getpid02.c index 86ad5a29..3da853d4 100755 --- a/testcases/kernel/syscalls/getpid/getpid02.c +++ b/testcases/kernel/syscalls/getpid/getpid02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Check that: * * - fork() in parent returns the same pid as getpid() in child diff --git a/testcases/kernel/syscalls/getppid/getppid01.c b/testcases/kernel/syscalls/getppid/getppid01.c index f37948dd..3f05aed3 100755 --- a/testcases/kernel/syscalls/getppid/getppid01.c +++ b/testcases/kernel/syscalls/getppid/getppid01.c @@ -1,22 +1,26 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) Linux Test Project, 2006-2023 */ /*\ - * [Description] - * * Test whether parent process id that getppid() returns is out of range. */ #include #include "tst_test.h" +static pid_t pid_max; + +static void setup(void) +{ + SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%d\n", &pid_max); +} + static void verify_getppid(void) { - pid_t ppid, pid_max; - - SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%d\n", &pid_max); + pid_t ppid; ppid = getppid(); if (ppid > pid_max) @@ -26,5 +30,6 @@ static void verify_getppid(void) } static struct tst_test test = { + .setup = setup, .test_all = verify_getppid, }; diff --git a/testcases/kernel/syscalls/getppid/getppid02.c b/testcases/kernel/syscalls/getppid/getppid02.c index cbc7ae76..7497eba1 100755 --- a/testcases/kernel/syscalls/getppid/getppid02.c +++ b/testcases/kernel/syscalls/getppid/getppid02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Check that getppid() in child returns the same pid as getpid() in parent. */ diff --git a/testcases/kernel/syscalls/getrandom/.gitignore b/testcases/kernel/syscalls/getrandom/.gitignore index ef06ece9..e47d8b3f 100755 --- a/testcases/kernel/syscalls/getrandom/.gitignore +++ b/testcases/kernel/syscalls/getrandom/.gitignore @@ -2,3 +2,4 @@ /getrandom02 /getrandom03 /getrandom04 +/getrandom05 diff --git a/testcases/kernel/syscalls/getrandom/getrandom05.c b/testcases/kernel/syscalls/getrandom/getrandom05.c new file mode 100644 index 00000000..f05a4fa8 --- /dev/null +++ b/testcases/kernel/syscalls/getrandom/getrandom05.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Copyright (c) Linux Test Project, 2024 + * Author: Yang Xu + */ + +/*\ + * Verify that getrandom(2) fails with + * + * - EFAULT when buf address is outside the accessible address space + * - EINVAL when flag is invalid + */ + +#include "tst_test.h" +#include "lapi/getrandom.h" +#include "getrandom_var.h" + +static char buff_efault[64]; +static char buff_einval[64]; + +static struct test_case_t { + char *buff; + size_t size; + unsigned int flag; + int expected_errno; + char *desc; +} tcases[] = { + {(void *)-1, sizeof(buff_efault), 0, EFAULT, + "buf address is outside the accessible address space"}, + {buff_einval, sizeof(buff_einval), -1, EINVAL, "flag is invalid"}, +}; + +static void setup(void) +{ + getrandom_info(); +} + +static void verify_getrandom(unsigned int i) +{ + struct test_case_t *tc = &tcases[i]; + + /* EFAULT test can segfault on recent glibc, skip it */ + if (tst_variant == 1 && tc->expected_errno == EFAULT) { + tst_res(TCONF, "Skipping EFAULT test for libc getrandom()"); + return; + } + + TST_EXP_FAIL2(do_getrandom(tc->buff, tc->size, tc->flag), + tc->expected_errno, "%s", tc->desc); +} + +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .test = verify_getrandom, + .test_variants = TEST_VARIANTS, + .setup = setup, +}; diff --git a/testcases/kernel/syscalls/getrandom/getrandom_var.h b/testcases/kernel/syscalls/getrandom/getrandom_var.h new file mode 100644 index 00000000..b19b0ebc --- /dev/null +++ b/testcases/kernel/syscalls/getrandom/getrandom_var.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 Jan Stancek + */ + +#ifndef GETRANDOM_VAR__ +#define GETRANDOM_VAR__ + +#include "lapi/syscalls.h" + +static inline int do_getrandom(void *buf, size_t buflen, unsigned int flags) +{ + switch (tst_variant) { + case 0: + return tst_syscall(__NR_getrandom, buf, buflen, flags); + case 1: + return getrandom(buf, buflen, flags); + } + return -1; +} + +static void getrandom_info(void) +{ + switch (tst_variant) { + case 0: + tst_res(TINFO, "Testing SYS_getrandom syscall"); + break; + case 1: + tst_res(TINFO, "Testing libc getrandom()"); + break; + } +} + +/* if we don't have libc getrandom() test only syscall version */ +#ifdef HAVE_SYS_RANDOM_H +# define TEST_VARIANTS 2 +#else +# define TEST_VARIANTS 1 +#endif + +#endif /* GETRANDOM_VAR__ */ diff --git a/testcases/kernel/syscalls/getresgid/Makefile b/testcases/kernel/syscalls/getresgid/Makefile index b2bb1e00..a319acf8 100755 --- a/testcases/kernel/syscalls/getresgid/Makefile +++ b/testcases/kernel/syscalls/getresgid/Makefile @@ -3,6 +3,9 @@ top_srcdir ?= ../../../.. +# Remove after rewriting all tests to the new API. +USE_LEGACY_COMPAT_16_H := 1 + include $(top_srcdir)/include/mk/testcases.mk include $(abs_srcdir)/../utils/compat_16.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/getresgid/getresgid01.c b/testcases/kernel/syscalls/getresgid/getresgid01.c index b5fb804e..8000200e 100755 --- a/testcases/kernel/syscalls/getresgid/getresgid01.c +++ b/testcases/kernel/syscalls/getresgid/getresgid01.c @@ -73,6 +73,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" char *TCID = "getresgid01"; diff --git a/testcases/kernel/syscalls/getresgid/getresgid02.c b/testcases/kernel/syscalls/getresgid/getresgid02.c index 8bddf982..ca4502aa 100755 --- a/testcases/kernel/syscalls/getresgid/getresgid02.c +++ b/testcases/kernel/syscalls/getresgid/getresgid02.c @@ -75,6 +75,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" #define LTPUSER "nobody" diff --git a/testcases/kernel/syscalls/getresgid/getresgid03.c b/testcases/kernel/syscalls/getresgid/getresgid03.c index 0785359e..1d7bcabd 100755 --- a/testcases/kernel/syscalls/getresgid/getresgid03.c +++ b/testcases/kernel/syscalls/getresgid/getresgid03.c @@ -77,6 +77,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" char *TCID = "getresgid03"; diff --git a/testcases/kernel/syscalls/getresuid/Makefile b/testcases/kernel/syscalls/getresuid/Makefile index b2bb1e00..a319acf8 100755 --- a/testcases/kernel/syscalls/getresuid/Makefile +++ b/testcases/kernel/syscalls/getresuid/Makefile @@ -3,6 +3,9 @@ top_srcdir ?= ../../../.. +# Remove after rewriting all tests to the new API. +USE_LEGACY_COMPAT_16_H := 1 + include $(top_srcdir)/include/mk/testcases.mk include $(abs_srcdir)/../utils/compat_16.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/getresuid/getresuid01.c b/testcases/kernel/syscalls/getresuid/getresuid01.c index 07fed9c1..a04918d6 100755 --- a/testcases/kernel/syscalls/getresuid/getresuid01.c +++ b/testcases/kernel/syscalls/getresuid/getresuid01.c @@ -72,6 +72,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" char *TCID = "getresuid01"; diff --git a/testcases/kernel/syscalls/getresuid/getresuid02.c b/testcases/kernel/syscalls/getresuid/getresuid02.c index 23f7944d..77896a8a 100755 --- a/testcases/kernel/syscalls/getresuid/getresuid02.c +++ b/testcases/kernel/syscalls/getresuid/getresuid02.c @@ -75,6 +75,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" #define LTPUSER "nobody" diff --git a/testcases/kernel/syscalls/getresuid/getresuid03.c b/testcases/kernel/syscalls/getresuid/getresuid03.c index bf117038..34e40c45 100755 --- a/testcases/kernel/syscalls/getresuid/getresuid03.c +++ b/testcases/kernel/syscalls/getresuid/getresuid03.c @@ -76,6 +76,11 @@ #include #include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting all tests to the new API. + */ #include "compat_16.h" char *TCID = "getresuid03"; diff --git a/testcases/kernel/syscalls/getrlimit/getrlimit01.c b/testcases/kernel/syscalls/getrlimit/getrlimit01.c index f14c8e6a..b31f827b 100755 --- a/testcases/kernel/syscalls/getrlimit/getrlimit01.c +++ b/testcases/kernel/syscalls/getrlimit/getrlimit01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that getrlimit(2) call will be successful for all possible resource * types. */ @@ -14,31 +12,32 @@ #include #include "tst_test.h" +#define RES(x) .res = x, .res_str = #x static struct tcase { int res; char *res_str; } tcases[] = { - {RLIMIT_CPU, "RLIMIT_CPU"}, - {RLIMIT_FSIZE, "RLIMIT_FSIZE"}, - {RLIMIT_DATA, "RLIMIT_DATA"}, - {RLIMIT_STACK, "RLIMIT_STACK"}, - {RLIMIT_CORE, "RLIMIT_CORE"}, - {RLIMIT_RSS, "RLIMIT_RSS"}, - {RLIMIT_NPROC, "RLIMIT_NPROC"}, - {RLIMIT_NOFILE, "RLIMIT_NOFILE"}, - {RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK"}, - {RLIMIT_AS, "RLIMIT_AS"}, - {RLIMIT_LOCKS, "RLIMIT_LOCKS"}, - {RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE"}, + {RES(RLIMIT_CPU)}, + {RES(RLIMIT_FSIZE)}, + {RES(RLIMIT_DATA)}, + {RES(RLIMIT_STACK)}, + {RES(RLIMIT_CORE)}, + {RES(RLIMIT_RSS)}, + {RES(RLIMIT_NPROC)}, + {RES(RLIMIT_NOFILE)}, + {RES(RLIMIT_MEMLOCK)}, + {RES(RLIMIT_AS)}, + {RES(RLIMIT_LOCKS)}, + {RES(RLIMIT_MSGQUEUE)}, #ifdef RLIMIT_NICE - {RLIMIT_NICE, "RLIMIT_NICE"}, + {RES(RLIMIT_NICE)}, #endif #ifdef RLIMIT_RTPRIO - {RLIMIT_RTPRIO, "RLIMIT_RTPRIO"}, + {RES(RLIMIT_RTPRIO)}, #endif - {RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING"}, + {RES(RLIMIT_SIGPENDING)}, #ifdef RLIMIT_RTTIME - {RLIMIT_RTTIME, "RLIMIT_RTTIME"}, + {RES(RLIMIT_RTTIME)}, #endif }; @@ -47,9 +46,8 @@ static void verify_getrlimit(unsigned int i) struct rlimit rlim; struct tcase *tc = &tcases[i]; - TST_EXP_PASS(getrlimit(tc->res, &rlim), - "getrlimit() test for %s", - tc->res_str); + TST_EXP_PASS(getrlimit(tc->res, &rlim), "getrlimit(%s, &rlim)", + tc->res_str); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/getrlimit/getrlimit02.c b/testcases/kernel/syscalls/getrlimit/getrlimit02.c index 9b68ce20..0d7c2bb3 100755 --- a/testcases/kernel/syscalls/getrlimit/getrlimit02.c +++ b/testcases/kernel/syscalls/getrlimit/getrlimit02.c @@ -5,14 +5,11 @@ */ /*\ - * [Description] + * Verify that, getrlimit(2) returns -1 and sets errno to * - * Test for checking error conditions for getrlimit(2) - * 1) getrlimit(2) returns -1 and sets errno to EFAULT if an invalid - * address is given for address parameter. - * 2) getrlimit(2) returns -1 and sets errno to EINVAL if an invalid - * resource type (RLIM_NLIMITS is a out of range resource type) is - * passed. + * - EFAULT if an invalid address is given for address parameter. + * - EINVAL if an invalid resource type (RLIM_NLIMITS is a out of + * range resource type) is passed. */ #include diff --git a/testcases/kernel/syscalls/getrlimit/getrlimit03.c b/testcases/kernel/syscalls/getrlimit/getrlimit03.c index 604082cc..846ef3ce 100755 --- a/testcases/kernel/syscalls/getrlimit/getrlimit03.c +++ b/testcases/kernel/syscalls/getrlimit/getrlimit03.c @@ -162,6 +162,12 @@ static void run(unsigned int resource) errno = 0; ret_ul = getrlimit_ulong(resource, &rlim_ul); errno_ul = errno; + if (errno_ul == ENOSYS) { + tst_res(TCONF | TERRNO, + "%s not implemented", __NR_getrlimit_ulong_str); + test.tcnt = 1; + return; + } if (compare_retval(resource, ret_u64, errno_u64, ret_ul, errno_ul, __NR_getrlimit_ulong_str) || diff --git a/testcases/kernel/syscalls/getrusage/getrusage01.c b/testcases/kernel/syscalls/getrusage/getrusage01.c index e0a7e28f..78b8e57a 100755 --- a/testcases/kernel/syscalls/getrusage/getrusage01.c +++ b/testcases/kernel/syscalls/getrusage/getrusage01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * AUTHOR: Saji Kumar.V.R @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test that getrusage() with RUSAGE_SELF and RUSAGE_CHILDREN succeeds. */ diff --git a/testcases/kernel/syscalls/getrusage/getrusage02.c b/testcases/kernel/syscalls/getrusage/getrusage02.c index 76766309..5138d96e 100755 --- a/testcases/kernel/syscalls/getrusage/getrusage02.c +++ b/testcases/kernel/syscalls/getrusage/getrusage02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that getrusage() fails with: * * - EINVAL with invalid who diff --git a/testcases/kernel/syscalls/getrusage/getrusage03.c b/testcases/kernel/syscalls/getrusage/getrusage03.c index fc14e93c..bc196c15 100755 --- a/testcases/kernel/syscalls/getrusage/getrusage03.c +++ b/testcases/kernel/syscalls/getrusage/getrusage03.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2011 Red Hat, Inc. * Copyright (C) 2021 Xie Ziyao */ /*\ - * [Description] - * * Test ru_maxrss behaviors in struct rusage. * * This test program is backported from upstream commit: 1f10206cf8e9, which diff --git a/testcases/kernel/syscalls/getrusage/getrusage03.h b/testcases/kernel/syscalls/getrusage/getrusage03.h index b28b9d4c..58a98b43 100755 --- a/testcases/kernel/syscalls/getrusage/getrusage03.h +++ b/testcases/kernel/syscalls/getrusage/getrusage03.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0-only * Copyright (C) 2011 Red Hat, Inc. * Copyright (C) 2021 Xie Ziyao */ @@ -6,10 +6,19 @@ #ifndef LTP_GETRUSAGE03_H #define LTP_GETRUSAGE03_H +#include #include "tst_test.h" #define DELTA_MAX 20480 +static void force_context_switches(int iterations) +{ + tst_res(TINFO, "Forcing context switch %d times", iterations); + + for (int i = 0; i < iterations; i++) + sched_yield(); +} + static void consume_mb(int consume_nr) { void *ptr; @@ -22,6 +31,8 @@ static void consume_mb(int consume_nr) ptr = SAFE_MALLOC(size); memset(ptr, 0, size); + force_context_switches(10); + SAFE_FILE_LINES_SCANF("/proc/self/status", "VmSwap: %lu", &vmswap_size); if (vmswap_size > 0) tst_brk(TBROK, "VmSwap is not zero"); diff --git a/testcases/kernel/syscalls/getrusage/getrusage03_child.c b/testcases/kernel/syscalls/getrusage/getrusage03_child.c index e8252084..1c6a19e3 100755 --- a/testcases/kernel/syscalls/getrusage/getrusage03_child.c +++ b/testcases/kernel/syscalls/getrusage/getrusage03_child.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2011 Red Hat, Inc. * Copyright (C) 2021 Xie Ziyao */ /*\ - * [Description] - * * Child program executed by getrusage03. */ diff --git a/testcases/kernel/syscalls/getrusage/getrusage04.c b/testcases/kernel/syscalls/getrusage/getrusage04.c index b03bc549..f18343c2 100755 --- a/testcases/kernel/syscalls/getrusage/getrusage04.c +++ b/testcases/kernel/syscalls/getrusage/getrusage04.c @@ -198,9 +198,9 @@ static void setup(void) { tst_sig(NOFORK, DEF_HANDLER, cleanup); - if (tst_is_virt(VIRT_XEN) || tst_is_virt(VIRT_KVM) || tst_is_virt(VIRT_HYPERV)) - tst_brkm(TCONF, NULL, "This testcase is not supported on this" - " virtual machine."); + if (tst_is_virt(VIRT_ANY)) + tst_brkm(TCONF, NULL, + "Test is not supported on a virtual machine"); BIAS_MAX = guess_timer_resolution(); diff --git a/testcases/kernel/syscalls/getsid/getsid01.c b/testcases/kernel/syscalls/getsid/getsid01.c index 89f6a729..e0201d4f 100755 --- a/testcases/kernel/syscalls/getsid/getsid01.c +++ b/testcases/kernel/syscalls/getsid/getsid01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that session IDs returned by getsid() (with argument pid=0) * are same in parent and child process. */ diff --git a/testcases/kernel/syscalls/getsid/getsid02.c b/testcases/kernel/syscalls/getsid/getsid02.c index 14b63472..8a25dcaf 100755 --- a/testcases/kernel/syscalls/getsid/getsid02.c +++ b/testcases/kernel/syscalls/getsid/getsid02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that getsid(2) fails with ESRCH errno when there is no * process found with process ID pid. */ diff --git a/testcases/kernel/syscalls/getsockname/getsockname01.c b/testcases/kernel/syscalls/getsockname/getsockname01.c index cce1543f..108d7fe1 100755 --- a/testcases/kernel/syscalls/getsockname/getsockname01.c +++ b/testcases/kernel/syscalls/getsockname/getsockname01.c @@ -1,181 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -/* - * Test Name: getsockname01 +/*\ + * Verify that getsockname() returns the proper errno for various failure cases: * - * Test Description: - * Verify that getsockname() returns the proper errno for various failure cases - * - * Usage: - * getsockname01 [-c n] [-e] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * None. + * - EBADF on a not open file + * - ENOTSOCK on a file descriptor not linked to a socket + * - EFAULT on invalid socket buffer o invalid socklen + * - EINVALI on an invalid addrlen */ -#include -#include -#include -#include +#include "tst_test.h" -#include -#include -#include -#include +static struct sockaddr_in sin0, fsin1; +static int sock_null, sock_bind, sock_fake; +static socklen_t sinlen; +static socklen_t sininval = -1; -#include - -#include "test.h" -#include "safe_macros.h" - -char *TCID = "getsockname01"; -int testno; - -int s; /* socket descriptor */ -struct sockaddr_in sin0, fsin1; -socklen_t sinlen; - -void setup(void), setup0(void), setup1(void), -cleanup(void), cleanup0(void), cleanup1(void); - -struct test_case_t { /* test case structure */ - int domain; /* PF_INET, PF_UNIX, ... */ - int type; /* SOCK_STREAM, SOCK_DGRAM ... */ - int proto; /* protocol number (usually 0 = default) */ - struct sockaddr *sockaddr; /* socket address buffer */ - socklen_t *salen; /* getsockname's 3rd argument */ - int retval; /* syscall return value */ - int experrno; /* expected errno */ - void (*setup) (void); - void (*cleanup) (void); +static struct test_case { + int *sock; + struct sockaddr_in *sockaddr; + socklen_t *addrlen; + int experrno; char *desc; -} tdat[] = { - { - PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&fsin1, - &sinlen, -1, EBADF, setup0, cleanup0, - "bad file descriptor"}, { - PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&fsin1, - &sinlen, -1, ENOTSOCK, setup0, cleanup0, - "bad file descriptor"}, -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ - { - PF_INET, SOCK_STREAM, 0, NULL, - &sinlen, -1, EFAULT, setup1, cleanup1, - "invalid socket buffer"}, { - /* invalid salen test for aligned input */ - PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&fsin1, - NULL, -1, EFAULT, setup1, cleanup1, - "invalid aligned salen"}, { - /* invalid salen test for unaligned input */ - PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&fsin1, - (socklen_t *) 1, -1, EFAULT, setup1, cleanup1, - "invalid unaligned salen"}, -#endif +} tcases[] = { + { .sock = &sock_fake, .sockaddr = &fsin1, .addrlen = &sinlen, + .experrno = EBADF, "bad file descriptor"}, + { .sock = &sock_null, .sockaddr = &fsin1, .addrlen = &sinlen, + .experrno = ENOTSOCK, "bad file descriptor"}, + { .sock = &sock_bind, .sockaddr = NULL, .addrlen = &sinlen, + .experrno = EFAULT, "invalid socket buffer"}, + { .sock = &sock_bind, .sockaddr = &fsin1, .addrlen = NULL, + .experrno = EFAULT, "invalid aligned salen"}, + { .sock = &sock_bind, .sockaddr = &fsin1, .addrlen = (socklen_t *) 1, + .experrno = EFAULT, "invalid unaligned salen"}, + { .sock = &sock_bind, .sockaddr = &fsin1, .addrlen = &sininval, + .experrno = EINVAL, "invalid socklen"}, }; -int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); - -int main(int argc, char *argv[]) +static void check_getsockname(unsigned int nr) { - int lc; + struct test_case *tc = &tcases[nr]; - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - tst_count = 0; - for (testno = 0; testno < TST_TOTAL; ++testno) { - tdat[testno].setup(); - - TEST(getsockname(s, tdat[testno].sockaddr, - tdat[testno].salen)); - if (TEST_RETURN != tdat[testno].retval || - (TEST_RETURN < 0 && - TEST_ERRNO != tdat[testno].experrno)) { - tst_resm(TFAIL, "%s ; returned" - " %ld (expected %d), errno %d (expected" - " %d)", tdat[testno].desc, - TEST_RETURN, tdat[testno].retval, - TEST_ERRNO, tdat[testno].experrno); - } else { - tst_resm(TPASS, "%s successful", - tdat[testno].desc); - } - tdat[testno].cleanup(); - } - } - cleanup(); - - tst_exit(); + TST_EXP_FAIL(getsockname(*(tc->sock), (struct sockaddr *) tc->sockaddr, + tc->addrlen), tc->experrno, "%s", tc->desc); } -void setup(void) +static void setup(void) { - TEST_PAUSE; - - /* initialize local sockaddr */ sin0.sin_family = AF_INET; sin0.sin_port = 0; sin0.sin_addr.s_addr = INADDR_ANY; + sock_fake = 400; + sock_null = SAFE_OPEN("/dev/null", O_WRONLY); + sock_bind = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); + SAFE_BIND(sock_bind, (struct sockaddr *)&sin0, sizeof(sin0)); + sinlen = sizeof(sin0); } -void cleanup(void) -{ -} - -void setup0(void) -{ - if (tdat[testno].experrno == EBADF) - s = 400; /* anything not an open file */ - else if ((s = open("/dev/null", O_WRONLY)) == -1) - tst_brkm(TBROK, cleanup, "error opening /dev/null - " - "errno: %s", strerror(errno)); - -} - -void cleanup0(void) -{ - s = -1; -} - -void setup1(void) -{ - s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type, - tdat[testno].proto); - SAFE_BIND(cleanup, s, (struct sockaddr *)&sin0, sizeof(sin0)); - sinlen = sizeof(fsin1); -} - -void cleanup1(void) -{ - (void)close(s); - s = -1; -} +static struct tst_test test = { + .setup = setup, + .test = check_getsockname, + .tcnt = ARRAY_SIZE(tcases), +}; diff --git a/testcases/kernel/syscalls/getsockopt/getsockopt01.c b/testcases/kernel/syscalls/getsockopt/getsockopt01.c index d1692fcd..2c1d371f 100755 --- a/testcases/kernel/syscalls/getsockopt/getsockopt01.c +++ b/testcases/kernel/syscalls/getsockopt/getsockopt01.c @@ -1,208 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (c) Linux Test Project, 2003-2017 + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -/* - * Test Name: getsockopt01 +/*\ + * Verify that getsockopt() returns the proper errno for various failure cases: * - * Test Description: - * Verify that getsockopt() returns the proper errno for various failure cases - * - * Usage: - * getsockopt01 [-c n] [-e] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * None. + * - EBADF on a not open file + * - ENOTSOCK on a file descriptor not linked to a socket + * - EFAULT on invalid address of value or length + * - EOPNOTSUPP on invalid option name or protocol + * - EINVAL on an invalid optlen */ -#include -#include -#include -#include +#include "tst_test.h" -#include -#include -#include -#include +static int sock_fake, sock_null, sock_bind; +static struct sockaddr_in sin0; +static int sinlen; +static int optval; +static socklen_t optlen; +static socklen_t optleninval = -1; -#include - -#include "test.h" -#include "safe_macros.h" - -char *TCID = "getsockopt01"; -int testno; - -int s; /* socket descriptor */ -struct sockaddr_in sin0, fsin1; -int sinlen; -int optval; -socklen_t optlen; - -void setup(void), setup0(void), setup1(void), -cleanup(void), cleanup0(void), cleanup1(void); - -struct test_case_t { /* test case structure */ - int domain; /* PF_INET, PF_UNIX, ... */ - int type; /* SOCK_STREAM, SOCK_DGRAM ... */ - int proto; /* protocol number (usually 0 = default) */ - int level; /* IPPROTO_* */ +static struct test_case { + int *sockfd; + int level; int optname; void *optval; socklen_t *optlen; - struct sockaddr *sin; - int salen; - int retval; /* syscall return value */ - int experrno; /* expected errno */ - void (*setup) (void); - void (*cleanup) (void); + int experrno; char *desc; -} tdat[] = { - { - PF_INET, SOCK_STREAM, 0, SOL_SOCKET, SO_OOBINLINE, &optval, - &optlen, (struct sockaddr *)&fsin1, sizeof(fsin1), - -1, EBADF, setup0, cleanup0, "bad file descriptor"} - , { - PF_INET, SOCK_STREAM, 0, SOL_SOCKET, SO_OOBINLINE, &optval, - &optlen, (struct sockaddr *)&fsin1, sizeof(fsin1), - -1, ENOTSOCK, setup0, cleanup0, "bad file descriptor"} - , -#ifndef UCLINUX - { - PF_INET, SOCK_STREAM, 0, SOL_SOCKET, SO_OOBINLINE, 0, &optlen, - (struct sockaddr *)&fsin1, sizeof(fsin1), -1, - EFAULT, setup1, cleanup1, "invalid option buffer"} - , { - PF_INET, SOCK_STREAM, 0, SOL_SOCKET, SO_OOBINLINE, &optval, 0, - (struct sockaddr *)&fsin1, sizeof(fsin1), -1, - EFAULT, setup1, cleanup1, "invalid optlen"} - , -#endif /* UCLINUX */ - { - PF_INET, SOCK_STREAM, 0, 500, SO_OOBINLINE, &optval, &optlen, - (struct sockaddr *)&fsin1, sizeof(fsin1), -1, - EOPNOTSUPP, setup1, cleanup1, "invalid level"} - , { - PF_INET, SOCK_STREAM, 0, IPPROTO_UDP, SO_OOBINLINE, &optval, - &optlen, (struct sockaddr *)&fsin1, sizeof(fsin1), - -1, EOPNOTSUPP, setup1, cleanup1, "invalid option name"} - , { - PF_INET, SOCK_STREAM, 0, IPPROTO_UDP, SO_OOBINLINE, &optval, - &optlen, (struct sockaddr *)&fsin1, sizeof(fsin1), - -1, EOPNOTSUPP, setup1, cleanup1, - "invalid option name (UDP)"} - , { - PF_INET, SOCK_STREAM, 0, IPPROTO_IP, -1, &optval, &optlen, - (struct sockaddr *)&fsin1, sizeof(fsin1), -1, - ENOPROTOOPT, setup1, cleanup1, "invalid option name (IP)"} - , { - PF_INET, SOCK_STREAM, 0, IPPROTO_TCP, -1, &optval, &optlen, - (struct sockaddr *)&fsin1, sizeof(fsin1), -1, - ENOPROTOOPT, setup1, cleanup1, "invalid option name (TCP)"} -,}; +} tcases[] = { + {.sockfd = &sock_fake, .level = SOL_SOCKET, .optname = SO_OOBINLINE, .optval = &optval, + .optlen = &optlen, .experrno = EBADF, .desc = "bad file descriptor"}, -int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); + {.sockfd = &sock_null, .level = SOL_SOCKET, .optname = SO_OOBINLINE, .optval = &optval, + .optlen = &optlen, .experrno = ENOTSOCK, .desc = "bad file descriptor"}, -int main(int argc, char *argv[]) + {.sockfd = &sock_bind, .level = SOL_SOCKET, .optname = SO_OOBINLINE, .optval = 0, + .optlen = &optlen, .experrno = EFAULT, .desc = "invalid option buffer"}, + + {.sockfd = &sock_bind, .level = SOL_SOCKET, .optname = SO_OOBINLINE, .optval = &optval, + .optlen = 0, .experrno = EFAULT, .desc = "invalid optlen"}, + + {.sockfd = &sock_bind, .level = 500, .optname = SO_OOBINLINE, .optval = &optval, + .optlen = &optlen, .experrno = EOPNOTSUPP, .desc = "invalid level"}, + + {.sockfd = &sock_bind, .level = IPPROTO_UDP, .optname = SO_OOBINLINE, .optval = &optval, + .optlen = &optlen, .experrno = EOPNOTSUPP, .desc = "not supported option name (UDP)"}, + + {.sockfd = &sock_bind, .level = IPPROTO_IP, .optname = -1, .optval = &optval, + .optlen = &optlen, .experrno = ENOPROTOOPT, .desc = "invalid option name (IP)"}, + + {.sockfd = &sock_bind, .level = IPPROTO_TCP, .optname = -1, .optval = &optval, + .optlen = &optlen, .experrno = ENOPROTOOPT, .desc = "invalid option name (TCP)"}, + + {.sockfd = &sock_bind, .level = SOL_SOCKET, .optname = SO_OOBINLINE, .optval = &optval, + .optlen = &optleninval, .experrno = EINVAL, .desc = "invalid optlen"}, +}; + + +static void check_getsockopt(unsigned int nr) { - int lc; + struct test_case *tc = &tcases[nr]; - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - tst_count = 0; - for (testno = 0; testno < TST_TOTAL; ++testno) { - tdat[testno].setup(); - - TEST(getsockopt(s, tdat[testno].level, - tdat[testno].optname, - tdat[testno].optval, - tdat[testno].optlen)); - if (TEST_RETURN != tdat[testno].retval || - (TEST_RETURN < 0 && - TEST_ERRNO != tdat[testno].experrno)) { - tst_resm(TFAIL, "%s ; returned" - " %ld (expected %d), errno %d (expected" - " %d)", tdat[testno].desc, - TEST_RETURN, tdat[testno].retval, - TEST_ERRNO, tdat[testno].experrno); - } else { - tst_resm(TPASS, "%s successful", - tdat[testno].desc); - } - tdat[testno].cleanup(); - } - } - cleanup(); - - tst_exit(); + TST_EXP_FAIL(getsockopt(*(tc->sockfd), tc->level, tc->optname, tc->optval, tc->optlen), + tc->experrno, "%s", tc->desc); } -void setup(void) +static void setup(void) { - TEST_PAUSE; - - /* initialize local sockaddr */ sin0.sin_family = AF_INET; sin0.sin_port = 0; sin0.sin_addr.s_addr = INADDR_ANY; -} - -void cleanup(void) -{ -} - -void setup0(void) -{ - if (tdat[testno].experrno == EBADF) - s = 400; /* anything not an open file */ - else if ((s = open("/dev/null", O_WRONLY)) == -1) - tst_brkm(TBROK, cleanup, "error opening /dev/null - " - "errno: %s", strerror(errno)); -} - -void cleanup0(void) -{ - s = -1; -} - -void setup1(void) -{ - s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type, - tdat[testno].proto); - SAFE_BIND(cleanup, s, (struct sockaddr *)&sin0, sizeof(sin0)); - sinlen = sizeof(fsin1); + sock_fake = 400; + sock_null = SAFE_OPEN("/dev/null", O_WRONLY); + sock_bind = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); + SAFE_BIND(sock_bind, (struct sockaddr *)&sin0, sizeof(sin0)); + sinlen = sizeof(sin0); optlen = sizeof(optval); } -void cleanup1(void) -{ - (void)close(s); - s = -1; -} +static struct tst_test test = { + .setup = setup, + .test = check_getsockopt, + .tcnt = ARRAY_SIZE(tcases), +}; diff --git a/testcases/kernel/syscalls/getsockopt/getsockopt02.c b/testcases/kernel/syscalls/getsockopt/getsockopt02.c index 47aef32a..68575799 100755 --- a/testcases/kernel/syscalls/getsockopt/getsockopt02.c +++ b/testcases/kernel/syscalls/getsockopt/getsockopt02.c @@ -3,9 +3,8 @@ * Copyright (C) 2017 Red Hat, Inc. */ -/* - * Test description: Test retrieving of peer credentials (SO_PEERCRED) - * +/*\ + * Test getsockopt(2) for retrieving peer credentials (SO_PEERCRED). */ #define _GNU_SOURCE @@ -53,6 +52,7 @@ static void test_function(void) tst_res(TFAIL | TERRNO, "Error with accepting connection"); goto clean; } + if (getsockopt(accepted, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) { tst_res(TFAIL | TERRNO, "Error while getting socket option"); @@ -67,6 +67,7 @@ static void test_function(void) clean: if (accepted >= 0) SAFE_CLOSE(accepted); + TST_CHECKPOINT_WAKE(0); } @@ -74,6 +75,7 @@ static void cleanup(void) { if (accepted >= 0) SAFE_CLOSE(accepted); + if (socket_fd >= 0) SAFE_CLOSE(socket_fd); } diff --git a/testcases/kernel/syscalls/gettid/.gitignore b/testcases/kernel/syscalls/gettid/.gitignore index 78dce349..9014f7c3 100755 --- a/testcases/kernel/syscalls/gettid/.gitignore +++ b/testcases/kernel/syscalls/gettid/.gitignore @@ -1 +1,2 @@ /gettid01 +/gettid02 diff --git a/testcases/kernel/syscalls/gettid/Makefile b/testcases/kernel/syscalls/gettid/Makefile index 4e9982f7..5345eb0f 100755 --- a/testcases/kernel/syscalls/gettid/Makefile +++ b/testcases/kernel/syscalls/gettid/Makefile @@ -10,7 +10,9 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk ifeq ($(ANDROID), 1) -FILTER_OUT_MAKE_TARGETS += gettid01 +FILTER_OUT_MAKE_TARGETS += gettid01 gettid02 endif +gettid02: LDLIBS += -lpthread + include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/gettid/gettid01.c b/testcases/kernel/syscalls/gettid/gettid01.c index 7e5b6b17..108b8266 100755 --- a/testcases/kernel/syscalls/gettid/gettid01.c +++ b/testcases/kernel/syscalls/gettid/gettid01.c @@ -1,96 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Crackerjack Project - * * Copyright (C) 2007-2008, Hitachi, Ltd. - * Author(s): Takahiro Yasui , - * Yumiko Sugita , - * Satoshi Fujiwara - * - * 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. - * - * $Id: gettid01.c,v 1.5 2009/10/26 14:55:47 subrata_modak Exp $ - * + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ +/*\ + * This test checks if parent pid is equal to tid in single-threaded + * application. */ -/* Porting from Crackerjack to LTP is done - by Masatake YAMATO */ +#include "tst_test.h" +#include "lapi/syscalls.h" -#include -#include -#include - -#include "test.h" - -void setup(); -void cleanup(); - -char *TCID = "gettid01"; - -int TST_TOTAL = 1; - -pid_t my_gettid(void) +static void run(void) { - return (pid_t) syscall(__NR_gettid); + long pid, tid; + + SAFE_FILE_LINES_SCANF("/proc/self/status", "Pid: %ld", &pid); + SAFE_FILE_LINES_SCANF("/proc/self/status", "Tgid: %ld", &tid); + + if (pid != tid) + tst_brk(TBROK, "Test function has been moved inside a thread?"); + + TST_EXP_EQ_LI(tst_syscall(__NR_gettid), tst_syscall(__NR_getpid)); + TST_EXP_EQ_LI(tst_syscall(__NR_gettid), pid); } -int main(int ac, char **av) -{ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - /* - * The following loop checks looping state if -c option given - */ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - TEST(my_gettid()); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "gettid() Failed, errno=%d: %s", - TEST_ERRNO, strerror(TEST_ERRNO)); - } else { - tst_resm(TPASS, "gettid() returned %ld", - TEST_RETURN); - } - } - - cleanup(); - tst_exit(); -} - -/* - * setup() - performs all ONE TIME setup for this test. - */ -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - -} - -/* - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - */ -void cleanup(void) -{ -} +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/syscalls/gettid/gettid02.c b/testcases/kernel/syscalls/gettid/gettid02.c new file mode 100644 index 00000000..88505068 --- /dev/null +++ b/testcases/kernel/syscalls/gettid/gettid02.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ +/*\ + * This test spawns multiple threads, then check for each one of them if the + * parent ID is different AND if the thread ID is different from all the other + * spwaned threads. + */ + +#include "tst_test.h" +#include "lapi/syscalls.h" +#include "tst_safe_pthread.h" + +#define THREADS_NUM 10 + +static volatile pid_t tids[THREADS_NUM]; + +static void *threaded(void *arg) +{ + int i = *(int *)arg; + pid_t pid, tid; + + pid = tst_syscall(__NR_getpid); + tid = tst_syscall(__NR_gettid); + + TST_EXP_EXPR(pid != tid, + "parent ID (%d) differs from thread[%d] ID (%d)", + pid, i, tid); + tids[i] = tid; + return NULL; +} + +static void run(void) +{ + pthread_t thread[THREADS_NUM]; + int args[THREADS_NUM]; + int error = 0; + + for (int i = 0; i < THREADS_NUM; i++) { + args[i] = i; + SAFE_PTHREAD_CREATE(&thread[i], NULL, threaded, &args[i]); + } + for (int i = 0; i < THREADS_NUM; i++) + SAFE_PTHREAD_JOIN(thread[i], NULL); + + for (int i = 0; i < THREADS_NUM; i++) { + for (int j = i + 1; j < THREADS_NUM; j++) { + if (tids[i] == tids[j]) { + tst_res(TINFO, "thread[%d] and thread[%d] have the same ID %d", i, j, tids[i]); + error = 1; + } + } + } + + if (error) + tst_res(TFAIL, "Some threads have the same TID"); + else + tst_res(TPASS, "All threads have a different TID"); +} + +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/syscalls/gettimeofday/gettimeofday01.c b/testcases/kernel/syscalls/gettimeofday/gettimeofday01.c index f9acb966..645cc6d3 100755 --- a/testcases/kernel/syscalls/gettimeofday/gettimeofday01.c +++ b/testcases/kernel/syscalls/gettimeofday/gettimeofday01.c @@ -1,107 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) Linux Test Project, 2001-2023 */ -/* - * NAME - * gettimeofday01.c +/*\ + * Test for gettimeofday error. * - * DESCRIPTION - * Testcase to check that gettimeofday(2) sets errno to EFAULT. - * - * ALGORITHM - * Call gettimeofday() with an invalid buffer, and expect EFAULT to be - * set in errno. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * NONE + * - EFAULT: tv pointed outside the accessible address space + * - EFAULT: tz pointed outside the accessible address space + * - EFAULT: both tv and tz pointed outside the accessible address space */ -#include -#include -#include "test.h" -#include +#include "tst_test.h" #include "lapi/syscalls.h" -char *TCID = "gettimeofday01"; -int TST_TOTAL = 1; +static struct timeval tv1; -#if !defined UCLINUX +static struct tcase { + void *tv; + void *tz; +} tcases[] = { + /* timezone structure is obsolete, tz should be treated as null */ + {(void *)-1, NULL}, + {&tv1, (void *)-1}, + {(void *)-1, (void *)-1}, +}; -void cleanup(void); -void setup(void); - -int main(int ac, char **av) +static void verify_gettimeofday(unsigned int n) { - int lc; - int ret; + struct tcase *tc = &tcases[n]; - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - TEST(tst_syscall(__NR_gettimeofday, (void *)-1, (void *)-1)); - - /* gettimeofday returns an int, so we need to turn the long - * TEST_RETURN into an int to test with */ - ret = TEST_RETURN; - if (ret != -1) { - tst_resm(TFAIL, - "call succeeded unexpectedly (got back %i, wanted -1)", - ret); - continue; - } - - if (TEST_ERRNO == EFAULT) - tst_resm(TPASS, - "gettimeofday(2) set the errno EFAULT correctly"); - else - tst_resm(TFAIL, - "gettimeofday(2) didn't set errno to EFAULT, errno=%i (%s)", - errno, strerror(errno)); - } - - cleanup(); - tst_exit(); + TST_EXP_FAIL(tst_syscall(__NR_gettimeofday, tc->tv, tc->tz), EFAULT); } -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - -void cleanup(void) -{ -} -#else - -int main(void) -{ - tst_brkm(TCONF, "gettimeofday EFAULT check disabled on uClinux"); -} - -#endif +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .test = verify_gettimeofday, +}; diff --git a/testcases/kernel/syscalls/gettimeofday/gettimeofday02.c b/testcases/kernel/syscalls/gettimeofday/gettimeofday02.c index 7c462cc2..1e0c9573 100755 --- a/testcases/kernel/syscalls/gettimeofday/gettimeofday02.c +++ b/testcases/kernel/syscalls/gettimeofday/gettimeofday02.c @@ -4,13 +4,11 @@ * Copyright (C) 2017 Cyril Hrubis */ -/* - * DESCRIPTION - * Check if gettimeofday is monotonous +/*\ + * Check if gettimeofday() is monotonous during 10s: * - * ALGORITHM - * Call gettimeofday() to get a t1 (fist value) - * call it again to get t2, see if t2 < t1, set t2 = t1, repeat for 10 sec + * - Call gettimeofday() to get a t1 (fist value) + * - Call it again to get t2, see if t2 < t1, set t2 = t1, repeat for 10 sec */ #include @@ -72,6 +70,6 @@ static void setup(void) static struct tst_test test = { .setup = setup, - .max_runtime = 10, + .runtime = 10, .test_all = verify_gettimeofday, }; diff --git a/testcases/kernel/syscalls/getuid/getuid01.c b/testcases/kernel/syscalls/getuid/getuid01.c index 206ca0c5..a25eb8d8 100755 --- a/testcases/kernel/syscalls/getuid/getuid01.c +++ b/testcases/kernel/syscalls/getuid/getuid01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Check the basic functionality of the getuid() system call. */ diff --git a/testcases/kernel/syscalls/getuid/getuid03.c b/testcases/kernel/syscalls/getuid/getuid03.c index a5deebf2..1de03d4e 100755 --- a/testcases/kernel/syscalls/getuid/getuid03.c +++ b/testcases/kernel/syscalls/getuid/getuid03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Check that getuid() return value matches value from /proc/self/status. */ diff --git a/testcases/kernel/syscalls/getxattr/getxattr01.c b/testcases/kernel/syscalls/getxattr/getxattr01.c index cec802a3..241cc1ea 100755 --- a/testcases/kernel/syscalls/getxattr/getxattr01.c +++ b/testcases/kernel/syscalls/getxattr/getxattr01.c @@ -1,142 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2011 Red Hat, Inc. - * - * 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. + * Copyright (c) Linux Test Project, 2012-2024 */ -/* +/*\ * Basic tests for getxattr(2) and make sure getxattr(2) handles error * conditions correctly. * - * There are 4 test cases: - * 1. Get an non-existing attribute, - * getxattr(2) should return -1 and set errno to ENODATA - * 2. Buffer size is smaller than attribute value size, - * getxattr(2) should return -1 and set errno to ERANGE - * 3. Get attribute, getxattr(2) should succeed - * 4. Verify the attribute got by getxattr(2) is same as the value we set + * 1. Get an non-existing attribute, getxattr(2) should return -1 and set errno + * to ENODATA. + * 2. Buffer size is smaller than attribute value size, getxattr(2) should + * return -1 and set errno to ERANGE. + * 3. Get attribute, getxattr(2) should succeed, and the attribute got by + * getxattr(2) should be same as the value we set. */ -#include "config.h" -#include -#include -#include -#include -#include -#include #include -#include -#ifdef HAVE_SYS_XATTR_H -# include -#endif -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" +#include -char *TCID = "getxattr01"; +#define XATTR_TEST_NOKEY "user.nosuchkey" +#define XATTR_TEST_KEY "user.testkey" +#define XATTR_TEST_VALUE "this is a test value" +#define XATTR_TEST_VALUE_SIZE 20 +#define BUFFSIZE 64 -#ifdef HAVE_SYS_XATTR_H -#define XATTR_TEST_KEY "user.testkey" -#define XATTR_TEST_VALUE "this is a test value" -#define XATTR_TEST_VALUE_SIZE 20 -#define BUFFSIZE 64 +static char filename[BUFSIZ]; -static void setup(void); -static void cleanup(void); - -char filename[BUFSIZ]; - -struct test_case { - char *fname; +static struct tcase { char *key; char *value; size_t size; int exp_err; -}; -struct test_case tc[] = { - { /* case 00, get non-existing attribute */ - .fname = filename, - .key = "user.nosuchkey", - .value = NULL, - .size = BUFFSIZE - 1, - .exp_err = ENODATA, - }, - { /* case 01, small value buffer */ - .fname = filename, - .key = XATTR_TEST_KEY, - .value = NULL, - .size = 1, - .exp_err = ERANGE, - }, - { /* case 02, get existing attribute */ - .fname = filename, - .key = XATTR_TEST_KEY, - .value = NULL, - .size = BUFFSIZE - 1, - .exp_err = 0, - }, +} tcases[] = { + { .key = XATTR_TEST_NOKEY, .size = BUFFSIZE - 1, .exp_err = ENODATA }, + { .key = XATTR_TEST_KEY, .size = 1, .exp_err = ERANGE }, + { .key = XATTR_TEST_KEY, .size = BUFFSIZE - 1 }, }; -int TST_TOTAL = sizeof(tc) / sizeof(tc[0]) + 1; - -int main(int argc, char *argv[]) +static void run(unsigned int n) { - int lc; - int i; + struct tcase *tc = &tcases[n]; - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; i < (int)ARRAY_SIZE(tc); i++) { - TEST(getxattr(tc[i].fname, tc[i].key, tc[i].value, - tc[i].size)); - - if (TEST_ERRNO == tc[i].exp_err) { - tst_resm(TPASS | TTERRNO, "expected behavior"); - } else { - tst_resm(TFAIL | TTERRNO, "unexpected behavior" - "- expected errno %d - Got", - tc[i].exp_err); - } - } - - if (TEST_RETURN != XATTR_TEST_VALUE_SIZE) { - tst_resm(TFAIL, - "getxattr() returned wrong size %ld expected %d", - TEST_RETURN, XATTR_TEST_VALUE_SIZE); - continue; - } - - if (memcmp(tc[i - 1].value, XATTR_TEST_VALUE, XATTR_TEST_VALUE_SIZE)) - tst_resm(TFAIL, "Wrong value, expect \"%s\" got \"%s\"", - XATTR_TEST_VALUE, tc[i - 1].value); - else - tst_resm(TPASS, "Got the right value"); + if (tc->exp_err) { + TST_EXP_FAIL(getxattr(filename, tc->key, tc->value, tc->size), + tc->exp_err); + return; } - cleanup(); - tst_exit(); + TST_EXP_VAL(getxattr(filename, tc->key, tc->value, tc->size), + XATTR_TEST_VALUE_SIZE); + + if (memcmp(tc->value, XATTR_TEST_VALUE, XATTR_TEST_VALUE_SIZE)) + tst_res(TFAIL, "Wrong value, expect '%s' got '%s'", + XATTR_TEST_VALUE, tc->value); + else + tst_res(TPASS, "getxattr() retrieved expected value"); } static void setup(void) @@ -144,41 +64,31 @@ static void setup(void) int fd; unsigned int i; - tst_require_root(); - - tst_tmpdir(); - /* Create test file and setup initial xattr */ snprintf(filename, BUFSIZ, "getxattr01testfile"); - fd = SAFE_CREAT(cleanup, filename, 0644); - close(fd); - if (setxattr(filename, XATTR_TEST_KEY, XATTR_TEST_VALUE, - strlen(XATTR_TEST_VALUE), XATTR_CREATE) == -1) { - if (errno == ENOTSUP) { - tst_brkm(TCONF, cleanup, "No xattr support in fs or " - "mount without user_xattr option"); - } - } + fd = SAFE_CREAT(filename, 0644); + SAFE_CLOSE(fd); - /* Prepare test cases */ - for (i = 0; i < ARRAY_SIZE(tc); i++) { - tc[i].value = malloc(BUFFSIZE); - if (tc[i].value == NULL) { - tst_brkm(TBROK | TERRNO, cleanup, - "Cannot allocate memory"); - } - } + SAFE_SETXATTR(filename, XATTR_TEST_KEY, XATTR_TEST_VALUE, + strlen(XATTR_TEST_VALUE), XATTR_CREATE); - TEST_PAUSE; + for (i = 0; i < ARRAY_SIZE(tcases); i++) + tcases[i].value = SAFE_MALLOC(BUFFSIZE); } static void cleanup(void) { - tst_rmdir(); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tcases); i++) + free(tcases[i].value); } -#else /* HAVE_SYS_XATTR_H */ -int main(int argc, char *argv[]) -{ - tst_brkm(TCONF, NULL, " does not exist."); -} -#endif + +static struct tst_test test = { + .needs_tmpdir = 1, + .needs_root = 1, + .setup = setup, + .cleanup = cleanup, + .tcnt = ARRAY_SIZE(tcases), + .test = run, +}; diff --git a/testcases/kernel/syscalls/getxattr/getxattr02.c b/testcases/kernel/syscalls/getxattr/getxattr02.c index a42057d0..245e5b00 100755 --- a/testcases/kernel/syscalls/getxattr/getxattr02.c +++ b/testcases/kernel/syscalls/getxattr/getxattr02.c @@ -1,64 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2011 Red Hat, Inc. - * - * 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. + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (c) Linux Test Project, 2012-2022 + * Copyright (c) 2023 Marius Kittler */ -/* +/*\ * In the user.* namespace, only regular files and directories can * have extended attributes. Otherwise getxattr(2) will return -1 * and set errno to ENODATA. * * There are 4 test cases: - * 1. Get attribute from a FIFO, setxattr(2) should return -1 and - * set errno to ENODATA - * 2. Get attribute from a char special file, setxattr(2) should - * return -1 and set errno to ENODATA - * 3. Get attribute from a block special file, setxattr(2) should - * return -1 and set errno to ENODATA - * 4. Get attribute from a UNIX domain socket, setxattr(2) should - * return -1 and set errno to ENODATA + * + * - Get attribute from a FIFO, setxattr(2) should return -1 and + * set errno to ENODATA + * - Get attribute from a char special file, setxattr(2) should + * return -1 and set errno to ENODATA + * - Get attribute from a block special file, setxattr(2) should + * return -1 and set errno to ENODATA + * - Get attribute from a UNIX domain socket, setxattr(2) should + * return -1 and set errno to ENODATA */ -#include "config.h" #include -#include #include -#include -#include -#include -#include -#include +#include #include #include -#include -#ifdef HAVE_SYS_XATTR_H -# include -#endif -#include "test.h" -#include "safe_macros.h" -char *TCID = "getxattr02"; +#include "tst_res_flags.h" +#include "tst_test.h" +#include "tst_test_macros.h" -#ifdef HAVE_SYS_XATTR_H +#define MNTPOINT "mntpoint" +#define FNAME MNTPOINT"/getxattr02" #define XATTR_TEST_KEY "user.testkey" #define FIFO "getxattr02fifo" @@ -66,94 +41,79 @@ char *TCID = "getxattr02"; #define BLK "getxattr02blk" #define SOCK "getxattr02sock" -static void setup(void); -static void cleanup(void); - -static char *tc[] = { - FIFO, /* case 00, get attr from fifo */ - CHR, /* case 01, get attr from char special */ - BLK, /* case 02, get attr from block special */ - SOCK, /* case 03, get attr from UNIX domain socket */ +static struct test_case { + const char *desc; + char *fname; + int mode; +} tcases[] = { + { + .desc = "get attr from fifo", + .fname = FNAME FIFO, + .mode = S_IFIFO, + }, + { + .desc = "get attr from char special", + .fname = FNAME CHR, + .mode = S_IFCHR, + }, + { + .desc = "get attr from block special", + .fname = FNAME BLK, + .mode = S_IFBLK, + }, + { + .desc = "get attr from UNIX domain socket", + .fname = FNAME SOCK, + .mode = S_IFSOCK, + }, }; -int TST_TOTAL = sizeof(tc) / sizeof(tc[0]); - -int main(int argc, char *argv[]) +static void run(unsigned int i) { - int lc; - int i; - int exp_eno; char buf[BUFSIZ]; + struct test_case *tc = &tcases[i]; + dev_t dev = tc->mode == S_IFCHR ? makedev(1, 3) : 0u; - tst_parse_opts(argc, argv, NULL, NULL); + if (mknod(tc->fname, tc->mode | 0777, dev) < 0) + tst_brk(TBROK | TERRNO, "create %s (mode %i) failed (%s)", + tc->fname, tc->mode, tc->desc); - setup(); + TEST(getxattr(tc->fname, XATTR_TEST_KEY, buf, BUFSIZ)); + if (TST_RET == -1 && TST_ERR == ENODATA) + tst_res(TPASS | TTERRNO, "%s: expected return value", + tc->desc); + else + tst_res(TFAIL | TTERRNO, + "%s: unexpected return value - expected errno %d - got", + tc->desc, ENODATA); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - exp_eno = ENODATA; - - for (i = 0; i < TST_TOTAL; i++) { - TEST(getxattr(tc[0], XATTR_TEST_KEY, buf, BUFSIZ)); - - if (TEST_RETURN == -1 && TEST_ERRNO == exp_eno) - tst_resm(TPASS | TTERRNO, "expected behavior"); - else - tst_resm(TFAIL | TTERRNO, "unexpected behavior" - " - expected errno %d - Got", exp_eno); - } - } - - cleanup(); - tst_exit(); + unlink(tc->fname); } static void setup(void) { - int fd; - dev_t dev; - - tst_require_root(); - - tst_tmpdir(); - - /* Test for xattr support */ - fd = SAFE_CREAT(cleanup, "testfile", 0644); - close(fd); - if (setxattr("testfile", "user.test", "test", 4, XATTR_CREATE) == -1) - if (errno == ENOTSUP) - tst_brkm(TCONF, cleanup, "No xattr support in fs or " - "mount without user_xattr option"); - unlink("testfile"); - - /* Create test files */ - if (mknod(FIFO, S_IFIFO | 0777, 0) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "Create FIFO(%s) failed", - FIFO); - - dev = makedev(1, 3); - if (mknod(CHR, S_IFCHR | 0777, dev) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "Create char special(%s)" - " failed", CHR); - - if (mknod(BLK, S_IFBLK | 0777, 0) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "Create block special(%s)" - " failed", BLK); - - if (mknod(SOCK, S_IFSOCK | 0777, 0) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "Create socket(%s) failed", - SOCK); - - TEST_PAUSE; + /* assert xattr support in the current filesystem */ + SAFE_TOUCH(FNAME, 0644, NULL); + TEST(setxattr(FNAME, "user.test", "test", 4, XATTR_CREATE)); + if (TST_ERR == ENOTSUP) + tst_brk(TCONF, + "No xattr support in fs or mount without user_xattr option"); + else if (TST_RET != 0) + tst_brk(TBROK | TTERRNO, "setxattr failed"); } -static void cleanup(void) -{ - tst_rmdir(); -} -#else /* HAVE_SYS_XATTR_H */ -int main(int argc, char *argv[]) -{ - tst_brkm(TCONF, NULL, " does not exist."); -} -#endif +static struct tst_test test = { + .timeout = 10, + .all_filesystems = 1, + .needs_root = 1, + .mntpoint = MNTPOINT, + .mount_device = 1, + .skip_filesystems = (const char *const []) { + "ramfs", + "nfs", + NULL + }, + .setup = setup, + .test = run, + .tcnt = ARRAY_SIZE(tcases) +}; diff --git a/testcases/kernel/syscalls/getxattr/getxattr03.c b/testcases/kernel/syscalls/getxattr/getxattr03.c index b6ea1428..e1f7bb6a 100755 --- a/testcases/kernel/syscalls/getxattr/getxattr03.c +++ b/testcases/kernel/syscalls/getxattr/getxattr03.c @@ -1,117 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2012 Red Hat, Inc. - * - * 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. + * Copyright (C) 2012 Red Hat, Inc. + * Copyright (c) 2023 Marius Kittler */ -/* - * An empty buffer of size zero can be passed into getxattr(2) to return - * the current size of the named extended attribute. +/*\ + * An empty buffer of size zero can be passed into getxattr(2) to + * return the current size of the named extended attribute. */ #include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_XATTR_H -# include -#endif -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" -char *TCID = "getxattr03"; +#include +#include "tst_safe_macros.h" -#ifdef HAVE_SYS_XATTR_H +#define MNTPOINT "mntpoint" +#define FNAME MNTPOINT"/getxattr03testfile" #define XATTR_TEST_KEY "user.testkey" #define XATTR_TEST_VALUE "test value" #define XATTR_TEST_VALUE_SIZE (sizeof(XATTR_TEST_VALUE) - 1) -#define TESTFILE "getxattr03testfile" -static void setup(void); -static void cleanup(void); - -int TST_TOTAL = 1; - -int main(int argc, char *argv[]) +static void run(void) { - int lc; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - TEST(getxattr(TESTFILE, XATTR_TEST_KEY, NULL, 0)); - - if (TEST_RETURN == XATTR_TEST_VALUE_SIZE) - tst_resm(TPASS, "getxattr(2) returned correct value"); - else - tst_resm(TFAIL | TTERRNO, "getxattr(2) failed"); - } - - cleanup(); - tst_exit(); + TST_EXP_VAL(getxattr(FNAME, XATTR_TEST_KEY, NULL, 0), + XATTR_TEST_VALUE_SIZE); } static void setup(void) { - int fd; - - tst_require_root(); - - tst_tmpdir(); - - /* Test for xattr support and set attr value */ - fd = SAFE_CREAT(cleanup, TESTFILE, 0644); - close(fd); - - if (setxattr(TESTFILE, XATTR_TEST_KEY, XATTR_TEST_VALUE, - XATTR_TEST_VALUE_SIZE, XATTR_CREATE) == -1) { - if (errno == ENOTSUP) - tst_brkm(TCONF, cleanup, "No xattr support in fs or " - "fs mounted without user_xattr option"); - else - tst_brkm(TBROK | TERRNO, cleanup, "setxattr %s failed", - TESTFILE); - } - - TEST_PAUSE; + SAFE_TOUCH(FNAME, 0644, NULL); + SAFE_SETXATTR(FNAME, XATTR_TEST_KEY, XATTR_TEST_VALUE, + XATTR_TEST_VALUE_SIZE, XATTR_CREATE); } -static void cleanup(void) -{ - tst_rmdir(); -} -#else /* HAVE_SYS_XATTR_H */ -int main(int argc, char *argv[]) -{ - tst_brkm(TCONF, NULL, " does not exist."); -} -#endif +static struct tst_test test = { + .timeout = 14, + .all_filesystems = 1, + .needs_root = 1, + .mntpoint = MNTPOINT, + .mount_device = 1, + .skip_filesystems = (const char *const []) { + "exfat", + "tmpfs", + "ramfs", + "nfs", + "vfat", + NULL + }, + .setup = setup, + .test_all = run, +}; diff --git a/testcases/kernel/syscalls/getxattr/getxattr04.c b/testcases/kernel/syscalls/getxattr/getxattr04.c index e64f2a1f..1fb59288 100755 --- a/testcases/kernel/syscalls/getxattr/getxattr04.c +++ b/testcases/kernel/syscalls/getxattr/getxattr04.c @@ -4,19 +4,14 @@ * Author: Xiao Yang */ -/* +/*\ * This is a regression test for the race between getting an existing * xattr and setting/removing a large xattr. This bug leads to that * getxattr() fails to get an existing xattr and returns ENOATTR in xfs * filesystem. * - * Thie bug has been fixed in: - * - * commit 5a93790d4e2df73e30c965ec6e49be82fc3ccfce - * Author: Brian Foster - * Date: Wed Jan 25 07:53:43 2017 -0800 - * - * xfs: remove racy hasattr check from attr ops + * This bug has been fixed in: + * 5a93790d4e2d ("xfs: remove racy hasattr check from attr ops") */ #include "config.h" @@ -108,7 +103,10 @@ static void setup(void) static struct tst_test test = { .needs_root = 1, .mount_device = 1, - .dev_fs_type = "xfs", + .filesystems = (struct tst_fs []){ + {.type = "xfs"}, + {} + }, .mntpoint = MNTPOINT, .forks_child = 1, .test_all = verify_getxattr, diff --git a/testcases/kernel/syscalls/getxattr/getxattr05.c b/testcases/kernel/syscalls/getxattr/getxattr05.c index 8d1752fd..5f0aac51 100755 --- a/testcases/kernel/syscalls/getxattr/getxattr05.c +++ b/testcases/kernel/syscalls/getxattr/getxattr05.c @@ -4,15 +4,16 @@ * Author: Xiao Yang */ -/* - * Description: - * 1) Witout a user namespace, getxattr(2) should get same data when - * acquiring the value of system.posix_acl_access twice. - * 2) With/Without mapped root UID in a user namespaces, getxattr(2) should - * get same data when acquiring the value of system.posix_acl_access twice. +/*\ + * This test verifies that: + * + * - Without a user namespace, getxattr(2) should get same data when + * acquiring the value of system.posix_acl_access twice. + * - With/Without mapped root UID in a user namespaces, getxattr(2) should + * get same data when acquiring the value of system.posix_acl_access twice. * * This issue included by getxattr05 has been fixed in kernel: - * '82c9a927bc5d ("getxattr: use correct xattr length")' + * 82c9a927bc5d ("getxattr: use correct xattr length") */ #define _GNU_SOURCE @@ -175,6 +176,10 @@ static struct tst_test test = { .cleanup = cleanup, .tcnt = ARRAY_SIZE(tcases), .test = do_getxattr, + .tags = (const struct tst_tag[]) { + {"linux-git", "82c9a927bc5d"}, + {} +}, }; #else /* HAVE_SYS_XATTR_H && HAVE_LIBACL*/ diff --git a/testcases/kernel/syscalls/init_module/.gitignore b/testcases/kernel/syscalls/init_module/.gitignore index 23baeb65..bb9f9825 100755 --- a/testcases/kernel/syscalls/init_module/.gitignore +++ b/testcases/kernel/syscalls/init_module/.gitignore @@ -2,10 +2,7 @@ /init_module02 /*.ko /*.mod.c -/*.ko.cmd -/*.mod.cmd -/*.mod.o.cmd -/*.o.cmd -/.built-in.a.cmd +/*.cmd /Module.symvers /modules.order +modules.livepatch diff --git a/testcases/kernel/syscalls/init_module/init_module.c b/testcases/kernel/syscalls/init_module/init_module.c index 78d03b89..f14cd80b 100755 --- a/testcases/kernel/syscalls/init_module/init_module.c +++ b/testcases/kernel/syscalls/init_module/init_module.c @@ -13,6 +13,8 @@ #include #include +#define DIRNAME "dummy_init" + static char status[20]; module_param_string(status, status, 20, 0444); @@ -23,14 +25,14 @@ static int dummy_init(void) if (!strcmp(status, "invalid")) return -EINVAL; - proc_dummy = proc_mkdir("dummy", 0); + proc_dummy = proc_mkdir(DIRNAME, 0); return 0; } module_init(dummy_init); static void dummy_exit(void) { - remove_proc_entry("dummy", 0); + remove_proc_entry(DIRNAME, 0); } module_exit(dummy_exit); diff --git a/testcases/kernel/syscalls/init_module/init_module01.c b/testcases/kernel/syscalls/init_module/init_module01.c index 26ff0b93..a0d4792c 100755 --- a/testcases/kernel/syscalls/init_module/init_module01.c +++ b/testcases/kernel/syscalls/init_module/init_module01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic init_module() tests. * * [Algorithm] @@ -13,6 +11,7 @@ * Inserts a simple module after opening and mmaping the module file. */ +#include #include #include "lapi/init_module.h" #include "tst_module.h" @@ -21,11 +20,15 @@ static struct stat sb; static void *buf; +static int sig_enforce; static void setup(void) { int fd; + if (tst_module_signature_enforced()) + sig_enforce = 1; + tst_module_exists(MODULE_NAME, NULL); fd = SAFE_OPEN(MODULE_NAME, O_RDONLY|O_CLOEXEC); @@ -36,6 +39,11 @@ static void setup(void) static void run(void) { + if (sig_enforce == 1) { + TST_EXP_FAIL(init_module(buf, sb.st_size, "status=valid"), EKEYREJECTED); + return; + } + TST_EXP_PASS(init_module(buf, sb.st_size, "status=valid")); if (!TST_PASS) return; diff --git a/testcases/kernel/syscalls/init_module/init_module02.c b/testcases/kernel/syscalls/init_module/init_module02.c index e6730e21..3fb1e871 100755 --- a/testcases/kernel/syscalls/init_module/init_module02.c +++ b/testcases/kernel/syscalls/init_module/init_module02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic init_module() failure tests. * * [Algorithm] @@ -14,6 +12,7 @@ */ #include +#include #include #include "lapi/init_module.h" #include "tst_module.h" @@ -22,7 +21,7 @@ #define MODULE_NAME "init_module.ko" static unsigned long size, zero_size; -static int kernel_lockdown, secure_boot; +static int kernel_lockdown, secure_boot, sig_enforce; static void *buf, *faulty_buf, *null_buf; static struct tst_cap cap_req = TST_CAP(TST_CAP_REQ, CAP_SYS_MODULE); @@ -44,6 +43,7 @@ static struct tcase { {"invalid_param", &buf, &size, "status=invalid", 0, 1, EINVAL}, {"no-perm", &buf, &size, "", 1, 0, EPERM}, {"module-exists", &buf, &size, "", 0, 1, EEXIST}, + {"module-unsigned", &buf, &size, "", 0, 1, EKEYREJECTED}, }; static void setup(void) @@ -51,6 +51,9 @@ static void setup(void) struct stat sb; int fd; + if (tst_module_signature_enforced()) + sig_enforce = 1; + tst_module_exists(MODULE_NAME, NULL); kernel_lockdown = tst_lockdown_enabled() > 0; @@ -73,6 +76,16 @@ static void run(unsigned int n) return; } + if ((sig_enforce == 1) && (tc->exp_errno != EKEYREJECTED)) { + tst_res(TCONF, "module signature is enforced, skipping %s", tc->name); + return; + } + + if ((sig_enforce != 1) && (tc->exp_errno == EKEYREJECTED)) { + tst_res(TCONF, "module signature is not enforced, skipping %s", tc->name); + return; + } + if (tc->cap) tst_cap_action(&cap_drop); diff --git a/testcases/kernel/syscalls/inotify/inotify01.c b/testcases/kernel/syscalls/inotify/inotify01.c index 2d82e597..4f7fa70e 100755 --- a/testcases/kernel/syscalls/inotify/inotify01.c +++ b/testcases/kernel/syscalls/inotify/inotify01.c @@ -2,13 +2,12 @@ /* * Copyright (c) 2007 SWSoft. All Rights Reserved. * Author: Andrew Vagin - * - * DESCRIPTION - * Check that inotify work for a file - * - * ALGORITHM - * Execute sequence file's operation and check return events */ + +/*\ + * Basic test for inotify events on file. + */ + #include "config.h" #include diff --git a/testcases/kernel/syscalls/inotify/inotify02.c b/testcases/kernel/syscalls/inotify/inotify02.c index cd1cb97b..709a9283 100755 --- a/testcases/kernel/syscalls/inotify/inotify02.c +++ b/testcases/kernel/syscalls/inotify/inotify02.c @@ -2,12 +2,10 @@ /* * Copyright (c) 2007 SWSoft. All Rights Reserved. * Author: Andrew Vagin - * - * DESCRIPTION - * Check that inotify work for a directory - * - * ALGORITHM - * Execute sequence file's operation and check return events + */ + +/*\ + * Basic test for inotify events on directory. */ #include "config.h" diff --git a/testcases/kernel/syscalls/inotify/inotify03.c b/testcases/kernel/syscalls/inotify/inotify03.c index 01a11554..9bb95add 100755 --- a/testcases/kernel/syscalls/inotify/inotify03.c +++ b/testcases/kernel/syscalls/inotify/inotify03.c @@ -2,13 +2,10 @@ /* * Copyright (c) 2008 Parallels. All Rights Reserved. * Author: Andrew Vagin - * - * DESCRIPTION - * Check that inotify get IN_UNMOUNT event and - * don't block the umount command. - * - * ALGORITHM - * Execute sequence file's operation and check return events + */ + +/*\ + * Check that inotify get IN_UNMOUNT event and don't block the umount command. */ #include "config.h" @@ -167,6 +164,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .needs_root = 1, .format_device = 1, .setup = setup, diff --git a/testcases/kernel/syscalls/inotify/inotify04.c b/testcases/kernel/syscalls/inotify/inotify04.c index 70c7fecf..1db38ddf 100755 --- a/testcases/kernel/syscalls/inotify/inotify04.c +++ b/testcases/kernel/syscalls/inotify/inotify04.c @@ -1,18 +1,20 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012 Linux Test Project. All Rights Reserved. * Ngie Cooper, April 2012 + */ + +/*\ + * Test for inotify IN_DELETE_SELF event. * - * DESCRIPTION - * verify that IN_DELETE_SELF functions as expected + * [Algorithm] * - * ALGORITHM - * This testcase creates a temporary directory, then add watches to a - * predefined file and subdirectory, and delete the file and directory to - * ensure that the IN_DELETE_SELF event is captured properly. + * This testcase creates a temporary directory, then add watches to a + * predefined file and subdirectory, and delete the file and directory + * to ensure that the IN_DELETE_SELF event is captured properly. * - * Because of how the inotify(7) API is designed, we also need to catch the - * IN_ATTRIB and IN_IGNORED events. + * Because of how the inotify(7) API is designed, we also need to catch + * the IN_ATTRIB and IN_IGNORED events. */ #include "config.h" diff --git a/testcases/kernel/syscalls/inotify/inotify05.c b/testcases/kernel/syscalls/inotify/inotify05.c index c83ff45a..d9bfb05f 100755 --- a/testcases/kernel/syscalls/inotify/inotify05.c +++ b/testcases/kernel/syscalls/inotify/inotify05.c @@ -2,14 +2,12 @@ /* * Copyright (c) 2014 SUSE Linux. All Rights Reserved. * Author: Jan Kara - * - * DESCRIPTION - * Check that inotify overflow event is properly generated - * - * ALGORITHM - * Generate enough events without reading them and check that overflow - * event is generated. */ + +/*\ + * Check that inotify overflow event is properly generated. + */ + #include "config.h" #include @@ -148,6 +146,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .needs_tmpdir = 1, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/inotify/inotify06.c b/testcases/kernel/syscalls/inotify/inotify06.c index 900bc1cb..42837ea0 100755 --- a/testcases/kernel/syscalls/inotify/inotify06.c +++ b/testcases/kernel/syscalls/inotify/inotify06.c @@ -2,8 +2,9 @@ /* * Copyright (c) 2015 SUSE Linux. All Rights Reserved. * Author: Jan Kara - * - * DESCRIPTION + */ + +/*\ * Test for inotify mark destruction race. * * Kernels prior to 4.2 have a race when inode is being deleted while @@ -11,7 +12,7 @@ * hit, the kernel crashes or loops. * * The problem has been fixed by commit: - * 8f2f3eb59dff "fsnotify: fix oops in fsnotify_clear_marks_by_group_flags()". + * 8f2f3eb59dff ("fsnotify: fix oops in fsnotify_clear_marks_by_group_flags()"). */ #include "config.h" @@ -113,7 +114,7 @@ static void cleanup(void) } static struct tst_test test = { - .max_runtime = 600, + .runtime = 600, .needs_root = 1, .needs_tmpdir = 1, .forks_child = 1, diff --git a/testcases/kernel/syscalls/inotify/inotify07.c b/testcases/kernel/syscalls/inotify/inotify07.c index a8820f0c..66a2f4d3 100755 --- a/testcases/kernel/syscalls/inotify/inotify07.c +++ b/testcases/kernel/syscalls/inotify/inotify07.c @@ -2,22 +2,24 @@ /* * Copyright (c) 2018 CTERA Networks. All Rights Reserved. * Author: Amir Goldstein + */ + +/*\ + * Check that inotify work for an overlayfs directory after copy up and + * drop caches. * - * DESCRIPTION - * Check that inotify work for an overlayfs directory after copy up and - * drop caches. + * An inotify watch pins the directory inode in cache, but not the dentry. + * The watch will not report events on the directory if overlayfs does not + * obtain the pinned inode to the new allocated dentry after drop caches. * - * An inotify watch pins the directory inode in cache, but not the dentry. - * The watch will not report events on the directory if overlayfs does not - * obtain the pinned inode to the new allocated dentry after drop caches. + * The problem has been fixed by commit: + * 31747eda41ef ("ovl: hash directory inodes for fsnotify"). * - * The problem has been fixed by commit: - * 31747eda41ef "ovl: hash directory inodes for fsnotify" + * [Algorithm] * - * ALGORITHM - * Add watch on an overlayfs lower directory then chmod directory and drop - * dentry and inode caches. Execute operations on directory and child and - * expect events to be reported on directory watch. + * Add watch on an overlayfs lower directory then chmod directory and drop + * dentry and inode caches. Execute operations on directory and child and + * expect events to be reported on directory watch. */ #include "config.h" @@ -178,6 +180,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .needs_root = 1, .mount_device = 1, .needs_overlay = 1, diff --git a/testcases/kernel/syscalls/inotify/inotify08.c b/testcases/kernel/syscalls/inotify/inotify08.c index 6a4278cb..4cbb16ce 100755 --- a/testcases/kernel/syscalls/inotify/inotify08.c +++ b/testcases/kernel/syscalls/inotify/inotify08.c @@ -2,22 +2,24 @@ /* * Copyright (c) 2018 CTERA Networks. All Rights Reserved. * Author: Amir Goldstein + */ + +/*\ + * Check that inotify work for an overlayfs file after copy up and + * drop caches. * - * DESCRIPTION - * Check that inotify work for an overlayfs file after copy up and - * drop caches. + * An inotify watch pins the file inode in cache, but not the dentry. + * The watch will not report events on the file if overlayfs does not + * obtain the pinned inode to the new allocated dentry after drop caches. * - * An inotify watch pins the file inode in cache, but not the dentry. - * The watch will not report events on the file if overlayfs does not - * obtain the pinned inode to the new allocated dentry after drop caches. + * The problem has been fixed by commit: + * 764baba80168 ("ovl: hash non-dir by lower inode for fsnotify"). * - * The problem has been fixed by commit: - * 764baba80168 "ovl: hash non-dir by lower inode for fsnotify" + * [Algorithm] * - * ALGORITHM - * Add watch on an overlayfs lower file then chmod file and drop dentry - * and inode caches. Execute operations on file and expect events to be - * reported on file watch. + * Add watch on an overlayfs lower file then chmod file and drop dentry + * and inode caches. Execute operations on file and expect events to be + * reported on file watch. */ #include "config.h" @@ -172,6 +174,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .needs_root = 1, .mount_device = 1, .needs_overlay = 1, diff --git a/testcases/kernel/syscalls/inotify/inotify09.c b/testcases/kernel/syscalls/inotify/inotify09.c index 4ce8e51d..e61808b6 100755 --- a/testcases/kernel/syscalls/inotify/inotify09.c +++ b/testcases/kernel/syscalls/inotify/inotify09.c @@ -3,8 +3,9 @@ * Copyright (c) 2018 SUSE Linux. All Rights Reserved. * Author: Jan Kara * Chnaged to use fzsync library by Cyril Hrubis - * - * DESCRIPTION + */ + +/*\ * Test for inotify mark connector destruction race. * * Kernels prior to 4.17 have a race when the last fsnotify mark on the inode @@ -12,7 +13,7 @@ * inode. When the race is hit, the kernel crashes or loops. * * The problem has been fixed by commit: - * d90a10e2444b "fsnotify: Fix fsnotify_mark_connector race" + * d90a10e2444b ("fsnotify: Fix fsnotify_mark_connector race"). */ #include "config.h" @@ -94,7 +95,8 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .test_all = verify_inotify, - .max_runtime = 150, + .runtime = 150, + .min_runtime = 2, .tags = (const struct tst_tag[]) { {"linux-git", "d90a10e2444b"}, {} diff --git a/testcases/kernel/syscalls/inotify/inotify10.c b/testcases/kernel/syscalls/inotify/inotify10.c index eb232209..a78572df 100755 --- a/testcases/kernel/syscalls/inotify/inotify10.c +++ b/testcases/kernel/syscalls/inotify/inotify10.c @@ -1,17 +1,18 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 CTERA Networks. All Rights Reserved. * * Started by Amir Goldstein - * - * DESCRIPTION - * Check that event is reported to watching parent and watching child - * based on their interest + */ + +/*\ + * Check that event is reported to watching parent and watching child + * based on their interest. * * Test case #3 is a regression test for commit fecc4559780d that fixes * a bug introduced in kernel v5.9: * - * fsnotify: fix events reported to watching parent and child + * fecc4559780d ("fsnotify: fix events reported to watching parent and child"). */ #include "config.h" diff --git a/testcases/kernel/syscalls/inotify/inotify11.c b/testcases/kernel/syscalls/inotify/inotify11.c index dd32ea7f..5af9cdb9 100644 --- a/testcases/kernel/syscalls/inotify/inotify11.c +++ b/testcases/kernel/syscalls/inotify/inotify11.c @@ -7,13 +7,12 @@ */ /*\ - * [Description] * Test opening files after receiving IN_DELETE. * * Kernel v5.13 has a regression allowing files to be open after IN_DELETE. * * The problem has been fixed by commit: - * a37d9a17f099 "fsnotify: invalidate dcache before IN_DELETE event". + * a37d9a17f099 ("fsnotify: invalidate dcache before IN_DELETE event"). */ #include "config.h" @@ -48,7 +47,7 @@ static void churn(void) char path[10]; int i; - for (i = 0; i <= CHURN_FILES; ++i) { + for (i = 0; i < CHURN_FILES; ++i) { snprintf(path, sizeof(path), "%d", i); SAFE_FILE_PRINTF(path, "1"); SAFE_UNLINK(path); @@ -118,6 +117,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 12, .needs_tmpdir = 1, .forks_child = 1, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/inotify/inotify12.c b/testcases/kernel/syscalls/inotify/inotify12.c index e658f4ae..5390b4c0 100644 --- a/testcases/kernel/syscalls/inotify/inotify12.c +++ b/testcases/kernel/syscalls/inotify/inotify12.c @@ -6,11 +6,10 @@ */ /*\ - * [Description] * Test special inotify mask flags. * * Regression test for kernel commit: - * a32e697cda27 ("inotify: show inotify mask flags in proc fdinfo") + * a32e697cda27 ("inotify: show inotify mask flags in proc fdinfo"). */ #include "config.h" @@ -152,7 +151,7 @@ static void cleanup(void) } static struct tst_test test = { - .max_runtime = 10, + .timeout = 10, .needs_tmpdir = 1, .cleanup = cleanup, .test = verify_inotify, diff --git a/testcases/kernel/syscalls/inotify_init/inotify_init1_01.c b/testcases/kernel/syscalls/inotify_init/inotify_init1_01.c index b2f582f8..04c66be5 100755 --- a/testcases/kernel/syscalls/inotify_init/inotify_init1_01.c +++ b/testcases/kernel/syscalls/inotify_init/inotify_init1_01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that inotify_init1() returns a file descriptor and sets * the close-on-exec (FD_CLOEXEC) flag on the new file descriptor * only when called with IN_CLOEXEC. diff --git a/testcases/kernel/syscalls/inotify_init/inotify_init1_02.c b/testcases/kernel/syscalls/inotify_init/inotify_init1_02.c index 3baf35e7..4c8d883a 100755 --- a/testcases/kernel/syscalls/inotify_init/inotify_init1_02.c +++ b/testcases/kernel/syscalls/inotify_init/inotify_init1_02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that inotify_init1() returns a file descriptor and sets the * O_NONBLOCK file status flag on the open file description referred * to by the new file descriptor only when called with IN_NONBLOCK. diff --git a/testcases/kernel/syscalls/io_cancel/io_cancel01.c b/testcases/kernel/syscalls/io_cancel/io_cancel01.c index f7e8bd06..699eb2a6 100755 --- a/testcases/kernel/syscalls/io_cancel/io_cancel01.c +++ b/testcases/kernel/syscalls/io_cancel/io_cancel01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test io_cancel invoked via syscall(2) with one of pointers set to invalid * address and expects it to return EFAULT. */ diff --git a/testcases/kernel/syscalls/io_cancel/io_cancel02.c b/testcases/kernel/syscalls/io_cancel/io_cancel02.c index 92ec6511..0394e308 100755 --- a/testcases/kernel/syscalls/io_cancel/io_cancel02.c +++ b/testcases/kernel/syscalls/io_cancel/io_cancel02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test io_cancel invoked via libaio with one of the data structures points * to invalid data and expects it to return -EFAULT. */ diff --git a/testcases/kernel/syscalls/io_destroy/io_destroy01.c b/testcases/kernel/syscalls/io_destroy/io_destroy01.c index 79c685fa..ce4158c2 100755 --- a/testcases/kernel/syscalls/io_destroy/io_destroy01.c +++ b/testcases/kernel/syscalls/io_destroy/io_destroy01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test io_destroy invoked via libaio with invalid ctx and expects it to * return -EINVAL. */ diff --git a/testcases/kernel/syscalls/io_destroy/io_destroy02.c b/testcases/kernel/syscalls/io_destroy/io_destroy02.c index c8cc6381..84868f31 100755 --- a/testcases/kernel/syscalls/io_destroy/io_destroy02.c +++ b/testcases/kernel/syscalls/io_destroy/io_destroy02.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Test io_destroy invoked via syscall(2) with an invalid ctx and expects * it to return EINVAL. */ diff --git a/testcases/kernel/syscalls/io_getevents/io_getevents01.c b/testcases/kernel/syscalls/io_getevents/io_getevents01.c index e8a426ab..0806ea91 100755 --- a/testcases/kernel/syscalls/io_getevents/io_getevents01.c +++ b/testcases/kernel/syscalls/io_getevents/io_getevents01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test io_getevents invoked via syscall(2) with invalid ctx and expects it to * return EINVAL. */ diff --git a/testcases/kernel/syscalls/io_getevents/io_getevents02.c b/testcases/kernel/syscalls/io_getevents/io_getevents02.c index 09eedd4f..6257ca82 100755 --- a/testcases/kernel/syscalls/io_getevents/io_getevents02.c +++ b/testcases/kernel/syscalls/io_getevents/io_getevents02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test io_getevents invoked via libaio with invalid ctx and expects it to * return -EINVAL. */ diff --git a/testcases/kernel/syscalls/io_setup/io_setup01.c b/testcases/kernel/syscalls/io_setup/io_setup01.c index ed99f387..3536b068 100755 --- a/testcases/kernel/syscalls/io_setup/io_setup01.c +++ b/testcases/kernel/syscalls/io_setup/io_setup01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test io_setup invoked via libaio: * * - io_setup succeeds if both nr_events and ctxp are valid. diff --git a/testcases/kernel/syscalls/io_setup/io_setup02.c b/testcases/kernel/syscalls/io_setup/io_setup02.c index f2979792..14a89d60 100755 --- a/testcases/kernel/syscalls/io_setup/io_setup02.c +++ b/testcases/kernel/syscalls/io_setup/io_setup02.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Test io_setup invoked via syscall(2): * * - io_setup fails and returns EFAULT if ctxp is NULL. diff --git a/testcases/kernel/syscalls/io_submit/Makefile b/testcases/kernel/syscalls/io_submit/Makefile index ce4f13b7..7d39a369 100755 --- a/testcases/kernel/syscalls/io_submit/Makefile +++ b/testcases/kernel/syscalls/io_submit/Makefile @@ -5,6 +5,6 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk -LDLIBS += $(AIO_LIBS) +io_submit01: LDLIBS += $(AIO_LIBS) include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/io_submit/io_submit01.c b/testcases/kernel/syscalls/io_submit/io_submit01.c index 616380b9..a76a5b32 100755 --- a/testcases/kernel/syscalls/io_submit/io_submit01.c +++ b/testcases/kernel/syscalls/io_submit/io_submit01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test io_submit() invoked via libaio: * * - io_submit fails and returns -EINVAL if ctx is invalid. diff --git a/testcases/kernel/syscalls/io_submit/io_submit02.c b/testcases/kernel/syscalls/io_submit/io_submit02.c index 6ba4d99a..36e9c29f 100755 --- a/testcases/kernel/syscalls/io_submit/io_submit02.c +++ b/testcases/kernel/syscalls/io_submit/io_submit02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test io_submit invoked via syscall(2): * * 1. io_submit() returns the number of iocbs submitted. diff --git a/testcases/kernel/syscalls/io_submit/io_submit03.c b/testcases/kernel/syscalls/io_submit/io_submit03.c index 90780c0e..45903107 100755 --- a/testcases/kernel/syscalls/io_submit/io_submit03.c +++ b/testcases/kernel/syscalls/io_submit/io_submit03.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test io_submit invoked via syscall(2): * * 1. io_submit fails and returns EINVAL if ctx is invalid. diff --git a/testcases/kernel/syscalls/io_uring/io_uring02.c b/testcases/kernel/syscalls/io_uring/io_uring02.c index c9d4bbcb..50206cf1 100755 --- a/testcases/kernel/syscalls/io_uring/io_uring02.c +++ b/testcases/kernel/syscalls/io_uring/io_uring02.c @@ -64,13 +64,12 @@ static struct msghdr beef_header = { static void setup(void) { - char *tmpdir = tst_get_tmpdir(); + char *tmpdir = tst_tmpdir_path(); int ret; addr.sun_family = AF_UNIX; ret = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", tmpdir, SOCK_NAME); - free(tmpdir); if (ret >= (int)sizeof(addr.sun_path)) tst_brk(TBROK, "Tempdir path is too long"); diff --git a/testcases/kernel/syscalls/ioctl/.gitignore b/testcases/kernel/syscalls/ioctl/.gitignore index 5fff7a61..dac4583f 100755 --- a/testcases/kernel/syscalls/ioctl/.gitignore +++ b/testcases/kernel/syscalls/ioctl/.gitignore @@ -7,6 +7,7 @@ /ioctl07 /ioctl08 /ioctl09 +/ioctl10 /ioctl_loop01 /ioctl_loop02 /ioctl_loop03 @@ -22,3 +23,16 @@ /ioctl_ns06 /ioctl_ns07 /ioctl_sg01 +/ioctl_ficlone01 +/ioctl_ficlone02 +/ioctl_ficlone03 +/ioctl_ficlone04 +/ioctl_ficlonerange01 +/ioctl_ficlonerange02 +/ioctl_fiemap01 +/ioctl_pidfd01 +/ioctl_pidfd02 +/ioctl_pidfd03 +/ioctl_pidfd04 +/ioctl_pidfd05 +/ioctl_pidfd06 diff --git a/testcases/kernel/syscalls/ioctl/ioctl01.c b/testcases/kernel/syscalls/ioctl/ioctl01.c index c84a72b9..42b93ac9 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl01.c +++ b/testcases/kernel/syscalls/ioctl/ioctl01.c @@ -2,14 +2,12 @@ /* * Copyright (c) International Business Machines Corp., 2001 * Copyright (c) 2020 Petr Vorel - * Copyright (c) Linux Test Project, 2002-2023 + * Copyright (c) Linux Test Project, 2002-2024 * 07/2001 Ported by Wayne Boyer * 04/2002 Fixes by wjhuie */ /*\ - * [Description] - * * Testcase to check the errnos set by the ioctl(2) system call. * * - EBADF: Pass an invalid fd to ioctl(fd, ...) and expect EBADF @@ -59,8 +57,42 @@ static struct tcase { static void verify_ioctl(unsigned int i) { - TST_EXP_FAIL(ioctl(*(tcases[i].fd), tcases[i].request, tcases[i].s_tio), - tcases[i].error, "%s", tcases[i].desc); + struct tcase *tc = &tcases[i]; + + TST_EXP_FAIL(ioctl(*(tc->fd), tc->request, tc->s_tio), tc->error, "%s", + tc->desc); +} + +static void test_bad_addr(unsigned int i) +{ + pid_t pid; + int status; + + pid = SAFE_FORK(); + if (!pid) { + verify_ioctl(i); + exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFEXITED(status) && !WEXITSTATUS(status)) + return; + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "Child killed by expected signal"); + return; + } + + tst_res(TFAIL, "Child %s", tst_strstatus(status)); +} + +static void do_test(unsigned int i) +{ + if (tcases[i].error == EFAULT) + test_bad_addr(i); + else + verify_ioctl(i); } static void setup(void) @@ -86,6 +118,7 @@ static struct tst_test test = { .needs_tmpdir = 1, .setup = setup, .cleanup = cleanup, - .test = verify_ioctl, - .tcnt = ARRAY_SIZE(tcases) + .test = do_test, + .tcnt = ARRAY_SIZE(tcases), + .forks_child = 1, }; diff --git a/testcases/kernel/syscalls/ioctl/ioctl02.c b/testcases/kernel/syscalls/ioctl/ioctl02.c index b4d4a359..7ef55af6 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl02.c +++ b/testcases/kernel/syscalls/ioctl/ioctl02.c @@ -1,468 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2001 - * Copyright (c) 2020 Petr Vorel - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) 2020 Petr Vorel + * Copyright (c) 2023 Marius Kittler */ -/* - * NAME - * ioctl02.c +/*\ + * Test TCGETA/TCGETS and TCSETA/TCSETS ioctl implementations for tty driver. * - * DESCRIPTION - * Testcase to test the TCGETA, and TCSETA ioctl implementations for - * the tty driver - * - * ALGORITHM - * In this test, the parent and child open the parentty and the childtty - * respectively. After opening the childtty the child flushes the stream - * and sends a SIGUSR1 to the parent (thereby asking it to continue its - * testing). The parent, which was waiting for this signal to arrive, now - * starts the testing. It issues a TCGETA ioctl to get all the tty - * parameters. It then changes them to known values by issuing a TCSETA - * ioctl. Then the parent issues a TCGETA ioctl again and compares the - * received values with what it had set earlier. The test fails if TCGETA - * or TCSETA fails, or if the received values don't match those that were - * set. The parent does all the testing, the requirement of the child - * process is to moniter the testing done by the parent, and hence the - * child just waits for the parent. - * - * USAGE: - * ioctl02 -D /dev/tty[0-9] [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * test must be run with the -D option - * test may have to be run as root depending on the tty permissions + * In this test, the parent and child open the parentty and the childtty + * respectively. After opening the childtty the child flushes the stream + * and wakes the parent (thereby asking it to continue its testing). The + * parent, then starts the testing. It issues a TCGETA/TCGETS ioctl to + * get all the tty parameters. It then changes them to known values by + * issuing a TCSETA/TCSETS ioctl. Then the parent issues a TCSETA/TCGETS + * ioctl again and compares the received values with what it had set + * earlier. The test fails if TCGETA/TCGETS or TCSETA/TCSETS fails, or if + * the received values don't match those that were set. The parent does + * all the testing, the requirement of the child process is to moniter + * the testing done by the parent, and hence the child just waits for the + * parent. */ #include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include +#include + #include "lapi/ioctl.h" +#include "tst_test.h" -#define CNUL 0 - -char *TCID = "ioctl02"; -int TST_TOTAL = 1; - -static struct termio termio, save_io; +static struct termio termio, termio_exp; +static struct termios termios, termios_exp, termios_bak; static char *parenttty, *childtty; -static int parentfd, childfd; +static int parentfd = -1; static int parentpid, childpid; -static volatile int sigterm, sigusr1, sigusr2; -static int closed = 1; -static int do_child_setup(void); -static int do_parent_setup(void); -static int run_ptest(void); -static int run_ctest(void); -static int chk_tty_parms(); +static void do_child(void); +static void prepare_termio(void); +static void run_ptest(void); +static void chk_tty_parms_termio(void); +static void chk_tty_parms_termios(void); static void setup(void); static void cleanup(void); -static void help(void); -static void do_child(void); -void do_child_uclinux(void); -static void sigterm_handler(void); -static int Devflag; -static char *devname; +static char *device; -static option_t options[] = { - {"D:", &Devflag, &devname}, - {NULL, NULL, NULL} +static struct variant { + const char *name; + void *termio, *termio_exp, *termio_bak; + size_t termio_size; + void (*check)(void); + int tcget, tcset; +} variants[] = { + { + .name = "termio", + .termio = &termio, + .termio_exp = &termio_exp, + .termio_size = sizeof(termio), + .check = &chk_tty_parms_termio, + .tcget = TCGETA, + .tcset = TCSETA, + }, + { + .name = "termios", + .termio = &termios, + .termio_exp = &termios_exp, + .termio_size = sizeof(termios), + .check = &chk_tty_parms_termios, + .tcget = TCGETS, + .tcset = TCSETS, + }, }; -int main(int ac, char **av) +static void verify_ioctl(void) { - int lc; - int rval; + tst_res(TINFO, "Testing %s variant", variants[tst_variant].name); - tst_parse_opts(ac, av, options, &help); + parenttty = device; + childtty = device; -#ifdef UCLINUX - maybe_run_child(&do_child_uclinux, "dS", &parentpid, &childtty); -#endif - - if (!Devflag) - tst_brkm(TBROK, NULL, "You must specify a tty device with " - "the -D option."); - - tst_require_root(); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - parenttty = devname; - childtty = devname; - - parentpid = getpid(); - - childpid = FORK_OR_VFORK(); - if (childpid < 0) - tst_brkm(TBROK, cleanup, "fork failed"); - - if (childpid == 0) { /* child */ -#ifdef UCLINUX - if (self_exec(av[0], "dS", parentpid, childtty) < 0) - tst_brkm(TBROK, cleanup, "self_exec failed"); -#else - do_child(); -#endif - } - - while (!sigusr1) - sleep(1); - - sigusr1 = 0; - - parentfd = do_parent_setup(); - if (parentfd < 0) { - kill(childpid, SIGTERM); - waitpid(childpid, NULL, 0); - cleanup(); - } - - /* run the parent test */ - rval = run_ptest(); - if (rval == -1) { - /* - * Parent cannot set/get ioctl parameters. - * SIGTERM the child and cleanup. - */ - kill(childpid, SIGTERM); - waitpid(childpid, NULL, 0); - cleanup(); - } - - if (rval != 0) - tst_resm(TFAIL, "TCGETA/TCSETA tests FAILED with " - "%d %s", rval, rval > 1 ? "errors" : "error"); - else - tst_resm(TPASS, "TCGETA/TCSETA tests SUCCEEDED"); - - /* FIXME: check return codes. */ - (void)kill(childpid, SIGTERM); - (void)waitpid(childpid, NULL, 0); - - /* - * Clean up things from the parent by restoring the - * tty device information that was saved in setup() - * and closing the tty file descriptor. - */ - if (ioctl(parentfd, TCSETA, &save_io) == -1) - tst_resm(TINFO, "ioctl restore failed in main"); - SAFE_CLOSE(cleanup, parentfd); - - closed = 1; + parentpid = getpid(); + childpid = SAFE_FORK(); + if (!childpid) { + do_child(); + exit(EXIT_SUCCESS); } - cleanup(); - tst_exit(); + TST_CHECKPOINT_WAIT(0); + + parentfd = SAFE_OPEN(parenttty, O_RDWR, 0777); + SAFE_IOCTL(parentfd, TCFLSH, TCIOFLUSH); + + run_ptest(); + + TST_CHECKPOINT_WAKE(0); + SAFE_CLOSE(parentfd); } -static void do_child(void) +static void prepare_termio(void) { - childfd = do_child_setup(); - if (childfd < 0) - _exit(1); - run_ctest(); - _exit(0); -} - -void do_child_uclinux(void) -{ - struct sigaction act; - - /* Set up the signal handlers again */ - act.sa_handler = (void *)sigterm_handler; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - (void)sigaction(SIGTERM, &act, 0); - - /* Run the normal child */ - do_child(); -} - -/* - * run_ptest() - setup the various termio structure values and issue - * the TCSETA ioctl call with the TEST macro. - */ -static int run_ptest(void) -{ - int i, rval; - /* Use "old" line discipline */ - termio.c_line = 0; + termios_exp.c_line = termio_exp.c_line = 0; /* Set control modes */ - termio.c_cflag = B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL; + termios_exp.c_cflag = termio_exp.c_cflag = B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL; /* Set control chars. */ - for (i = 0; i < NCC; i++) { - if (i == VEOL2) - continue; - termio.c_cc[i] = CSTART; - } + for (int i = 0; i < NCC; i++) + termio_exp.c_cc[i] = CSTART; + for (int i = 0; i < VEOL2; i++) + termios_exp.c_cc[i] = CSTART; /* Set local modes. */ - termio.c_lflag = + termios_exp.c_lflag = termio_exp.c_lflag = ((unsigned short)(ISIG | ICANON | XCASE | ECHO | ECHOE | NOFLSH)); /* Set input modes. */ - termio.c_iflag = + termios_exp.c_iflag = termio_exp.c_iflag = BRKINT | IGNPAR | INPCK | ISTRIP | ICRNL | IUCLC | IXON | IXANY | IXOFF; /* Set output modes. */ - termio.c_oflag = OPOST | OLCUC | ONLCR | ONOCR; - - TEST(ioctl(parentfd, TCSETA, &termio)); - - if (TEST_RETURN < 0) { - tst_resm(TFAIL, "ioctl TCSETA failed : " - "errno = %d", TEST_ERRNO); - return -1; - } - - /* Get termio and see if all parameters actually got set */ - rval = ioctl(parentfd, TCGETA, &termio); - if (rval < 0) { - tst_resm(TFAIL, "ioctl TCGETA failed. Ending test."); - return -1; - } - - return chk_tty_parms(); -} - -static int run_ctest(void) -{ - /* - * Wait till the parent has finished testing. - */ - while (!sigterm) - sleep(1); - - sigterm = 0; - - tst_resm(TINFO, "child: Got SIGTERM from parent."); - - if (close(childfd) == -1) - tst_resm(TINFO, "close() in run_ctest() failed"); - return 0; -} - -static int chk_tty_parms(void) -{ - int i, flag = 0; - - if (termio.c_line != 0) { - tst_resm(TINFO, "line discipline has incorrect value %o", - termio.c_line); - flag++; - } - /* - * The following Code Sniffet is disabled to check the value of c_cflag - * as it seems that due to some changes from 2.6.24 onwards, this - * setting is not done properly for either of (B50|CS7|CREAD|PARENB| - * PARODD|CLOCAL|(CREAD|HUPCL|CLOCAL). - * However, it has been observed that other flags are properly set. - */ -#if 0 - if (termio.c_cflag != (B50 | CS7 | CREAD | PARENB | PARODD | CLOCAL)) { - tst_resm(TINFO, "cflag has incorrect value. %o", - termio.c_cflag); - flag++; - } -#endif - - for (i = 0; i < NCC; i++) { - if (i == VEOL2) { - if (termio.c_cc[VEOL2] == CNUL) { - continue; - } else { - tst_resm(TINFO, "control char %d has " - "incorrect value %d %d", i, - termio.c_cc[i], CNUL); - flag++; - continue; - } - } - - if (termio.c_cc[i] != CSTART) { - tst_resm(TINFO, "control char %d has incorrect " - "value %d.", i, termio.c_cc[i]); - flag++; - } - } - - if (! - (termio.c_lflag - && (ISIG | ICANON | XCASE | ECHO | ECHOE | NOFLSH))) { - tst_resm(TINFO, "lflag has incorrect value. %o", - termio.c_lflag); - flag++; - } - - if (! - (termio.c_iflag - && (BRKINT | IGNPAR | INPCK | ISTRIP | ICRNL | IUCLC | IXON | IXANY - | IXOFF))) { - tst_resm(TINFO, "iflag has incorrect value. %o", - termio.c_iflag); - flag++; - } - - if (!(termio.c_oflag && (OPOST | OLCUC | ONLCR | ONOCR))) { - tst_resm(TINFO, "oflag has incorrect value. %o", - termio.c_oflag); - flag++; - } - - if (!flag) - tst_resm(TINFO, "termio values are set as expected"); - - return flag; -} - -static int do_parent_setup(void) -{ - int pfd; - - pfd = SAFE_OPEN(cleanup, parenttty, O_RDWR, 0777); - - /* unset the closed flag */ - closed = 0; - - /* flush tty queues to remove old output */ - SAFE_IOCTL(cleanup, pfd, TCFLSH, 2); - return pfd; -} - -static int do_child_setup(void) -{ - int cfd; - - cfd = open(childtty, O_RDWR, 0777); - if (cfd < 0) { - tst_resm(TINFO, "Could not open %s in do_child_setup(), errno " - "= %d", childtty, errno); - /* signal the parent so we don't hang the test */ - kill(parentpid, SIGUSR1); - return -1; - } - - /* flush tty queues to remove old output */ - if (ioctl(cfd, TCFLSH, 2) < 0) { - tst_resm(TINFO, "ioctl TCFLSH failed. : errno = %d", errno); - /* signal the parent so we don't hang the test */ - kill(parentpid, SIGUSR1); - return -1; - } - - /* tell the parent that we're done */ - kill(parentpid, SIGUSR1); - - return cfd; + termios_exp.c_oflag = termio_exp.c_oflag = OPOST | OLCUC | ONLCR | ONOCR; } /* - * Define the signals handlers here. + * run_ptest() - setup the various termio/termios structure values and issue + * the TCSETA/TCSETS ioctl call with the TEST macro. */ -static void sigterm_handler(void) +static void run_ptest(void) { - sigterm = 1; + struct variant *v = &variants[tst_variant]; + + /* Init termio/termios structures used to check if all params got set */ + memset(v->termio, 0, v->termio_size); + + SAFE_IOCTL(parentfd, v->tcset, v->termio_exp); + + /* Get termio and see if all parameters actually got set */ + SAFE_IOCTL(parentfd, v->tcget, v->termio); + v->check(); } -static void sigusr1_handler(void) +static int cmp_attr(unsigned long long exp, unsigned long long act, const char *attr) { - sigusr1 = 1; + if (act == exp) + return 0; + tst_res(TFAIL, "%s has incorrect value %llu, expected %llu", + attr, act, exp); + return 1; } -static void sigusr2_handler(void) +static int cmp_c_cc(unsigned char *exp_c_cc, unsigned char *act_c_cc, int ncc) { - sigusr2 = 1; + int i, fails = 0; + char what[32]; + + for (i = 0; i < ncc; ++i) { + sprintf(what, "control char %d", i); + fails += cmp_attr(exp_c_cc[i], act_c_cc[i], what); + } + return fails; } -static void help(void) +#define CMP_ATTR(term_exp, term, attr, flag) \ +({ \ + flag += cmp_attr((term_exp).attr, (term).attr, #attr); \ + flag; \ +}) + +#define CMP_C_CC(term_exp, term, flag) \ +({ \ + flag += cmp_c_cc(term_exp.c_cc, term.c_cc, sizeof(term.c_cc)); \ + flag; \ +}) + +static void chk_tty_parms_termio(void) { - printf(" -D : for example, /dev/tty[0-9]\n"); + int flag = 0; + + flag = CMP_ATTR(termio_exp, termio, c_line, flag); + flag = CMP_C_CC(termio_exp, termio, flag); + flag = CMP_ATTR(termio_exp, termio, c_lflag, flag); + flag = CMP_ATTR(termio_exp, termio, c_iflag, flag); + flag = CMP_ATTR(termio_exp, termio, c_oflag, flag); + + if (!flag) + tst_res(TPASS, "TCGETA/TCSETA tests"); +} + +static void chk_tty_parms_termios(void) +{ + int flag = 0; + + flag = CMP_ATTR(termios_exp, termios, c_line, flag); + flag = CMP_C_CC(termios_exp, termios, flag); + flag = CMP_ATTR(termios_exp, termios, c_lflag, flag); + flag = CMP_ATTR(termios_exp, termios, c_iflag, flag); + flag = CMP_ATTR(termios_exp, termios, c_oflag, flag); + + if (!flag) + tst_res(TPASS, "TCGETS/TCSETS tests"); +} + +static void do_child(void) +{ + int cfd = SAFE_OPEN(childtty, O_RDWR, 0777); + + SAFE_IOCTL(cfd, TCFLSH, TCIOFLUSH); + + /* tell the parent that we're done */ + TST_CHECKPOINT_WAKE(0); + + TST_CHECKPOINT_WAIT(0); + tst_res(TINFO, "child: parent has finished testing"); + SAFE_CLOSE(cfd); } static void setup(void) { - int fd; - struct sigaction act; + if (!device) + tst_brk(TBROK, "You must specify a tty device with -d option"); - /* XXX: TERRNO required all over the place */ - fd = SAFE_OPEN(NULL, devname, O_RDWR, 0777); + int fd = SAFE_OPEN(device, O_RDWR, 0777); - /* Save the current device information - to be restored in cleanup() */ - SAFE_IOCTL(cleanup, fd, TCGETA, &save_io); + SAFE_IOCTL(fd, TCGETS, &termios_bak); + SAFE_CLOSE(fd); - /* Close the device */ - SAFE_CLOSE(cleanup, fd); - - /* Set up the signal handlers */ - act.sa_handler = (void *)sigterm_handler; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - (void)sigaction(SIGTERM, &act, 0); - - act.sa_handler = (void *)sigusr1_handler; - act.sa_flags = 0; - (void)sigaction(SIGUSR1, &act, 0); - - act.sa_handler = (void *)sigusr2_handler; - act.sa_flags = 0; - (void)sigaction(SIGUSR2, &act, 0); - - act.sa_handler = SIG_IGN; - act.sa_flags = 0; - (void)sigaction(SIGTTOU, &act, 0); - - sigterm = sigusr1 = sigusr2 = 0; - - TEST_PAUSE; + prepare_termio(); } static void cleanup(void) { - if (!closed) { - if (ioctl(parentfd, TCSETA, &save_io) == -1) - tst_resm(TINFO, "ioctl restore failed in cleanup()"); - if (close(parentfd) == -1) - tst_resm(TINFO, "close() failed in cleanup()"); + if (parentfd >= 0) { + SAFE_IOCTL(parentfd, TCSETS, &termios_bak); + SAFE_CLOSE(parentfd); } } + +static struct tst_test test = { + .timeout = 9, + .needs_root = 1, + .needs_checkpoints = 1, + .forks_child = 1, + .setup = setup, + .cleanup = cleanup, + .test_all = verify_ioctl, + .test_variants = 2, + .options = (struct tst_option[]) { + {"d:", &device, "Tty device. For example, /dev/tty[0-9]"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl03.c b/testcases/kernel/syscalls/ioctl/ioctl03.c index 5a7a0a6a..8a868a98 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl03.c +++ b/testcases/kernel/syscalls/ioctl/ioctl03.c @@ -4,12 +4,12 @@ * Copyright (c) Linux Test Project, 2017-2019 * Author: Rusty Russell * Ported to LTP: subrata + * Test ioriginally written for kernel 2.6.27. */ -/* - * This program tests whether all the valid IFF flags are - * returned properly by implementation of TUNGETFEATURES ioctl - * on kernel 2.6.27 +/*\ + * Test whether all the valid IFF flags are returned properly by implementation + * of TUNGETFEATURES ioctl. */ #include diff --git a/testcases/kernel/syscalls/ioctl/ioctl04.c b/testcases/kernel/syscalls/ioctl/ioctl04.c index d9ddb8e5..952450ba 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl04.c +++ b/testcases/kernel/syscalls/ioctl/ioctl04.c @@ -2,7 +2,8 @@ /* * Copyright (c) 2017 Cyril Hrubis */ -/* + +/*\ * Basic test for the BLKROSET and BLKROGET ioctls. * * - Set the device read only, read the value back. @@ -81,6 +82,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .format_device = 1, .needs_root = 1, .setup = setup, diff --git a/testcases/kernel/syscalls/ioctl/ioctl05.c b/testcases/kernel/syscalls/ioctl/ioctl05.c index a53e8adb..586322e0 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl05.c +++ b/testcases/kernel/syscalls/ioctl/ioctl05.c @@ -2,7 +2,8 @@ /* * Copyright (c) 2017 Cyril Hrubis */ -/* + +/*\ * Basic test for the BLKGETSIZE and BLKGETSIZE64 ioctls. * * - BLKGETSIZE returns size in 512 byte blocks BLKGETSIZE64 in bytes diff --git a/testcases/kernel/syscalls/ioctl/ioctl06.c b/testcases/kernel/syscalls/ioctl/ioctl06.c index 6e56beee..4e4177ca 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl06.c +++ b/testcases/kernel/syscalls/ioctl/ioctl06.c @@ -2,7 +2,8 @@ /* * Copyright (c) 2017 Cyril Hrubis */ -/* + +/*\ * Basic test for the BLKRASET and BLKRAGET ioctls. * * Sets device read-ahead, reads it back and compares the values. diff --git a/testcases/kernel/syscalls/ioctl/ioctl07.c b/testcases/kernel/syscalls/ioctl/ioctl07.c index 073ff033..59ef893d 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl07.c +++ b/testcases/kernel/syscalls/ioctl/ioctl07.c @@ -2,11 +2,12 @@ /* * Copyright (c) 2017 Carlo Marcelo Arenas Belón */ -/* + +/*\ * Very basic test for the RND* ioctls. * * Reads the entropy available from both /proc and the ioctl and compares - * they are similar enough (within a configured fuzz factor) + * they are similar enough (within a configured fuzz factor). * */ diff --git a/testcases/kernel/syscalls/ioctl/ioctl08.c b/testcases/kernel/syscalls/ioctl/ioctl08.c index b877bf2a..78602f55 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl08.c +++ b/testcases/kernel/syscalls/ioctl/ioctl08.c @@ -2,18 +2,20 @@ /* * Copyright (c) 2019 SUSE LLC * Author: Christian Amann - * + */ + +/*\ * Tests the ioctl functionality to deduplicate fileranges using * btrfs filesystem. * - * 1) Sets the same contents for two files and deduplicates it. - * Deduplicates 3 bytes and set the status to - * FILE_DEDUPE_RANGE_SAME. - * 2) Sets different content for two files and tries to - * deduplicate it. 0 bytes get deduplicated and status is - * set to FILE_DEDUPE_RANGE_DIFFERS. - * 3) Sets same content for two files but sets the length to - * deduplicate to -1. ioctl(FIDEDUPERANGE) fails with EINVAL. + * 1. Sets the same contents for two files and deduplicates it. + * Deduplicates 3 bytes and set the status to + * FILE_DEDUPE_RANGE_SAME. + * 2. Sets different content for two files and tries to + * deduplicate it. 0 bytes get deduplicated and status is + * set to FILE_DEDUPE_RANGE_DIFFERS. + * 3. Sets same content for two files but sets the length to + * deduplicate to -1. ioctl(FIDEDUPERANGE) fails with EINVAL. */ #include "config.h" @@ -114,6 +116,7 @@ static void setup(void) static struct tst_test test = { + .timeout = 1, .test = verify_ioctl, .tcnt = ARRAY_SIZE(tcases), .setup = setup, @@ -122,7 +125,10 @@ static struct tst_test test = { .needs_root = 1, .mount_device = 1, .mntpoint = MNTPOINT, - .dev_fs_type = "btrfs", + .filesystems = (struct tst_fs []) { + {.type = "btrfs"}, + {} + }, .needs_drivers = (const char *const[]) { "btrfs", NULL, diff --git a/testcases/kernel/syscalls/ioctl/ioctl09.c b/testcases/kernel/syscalls/ioctl/ioctl09.c index 9728ecb9..f86355f2 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl09.c +++ b/testcases/kernel/syscalls/ioctl/ioctl09.c @@ -2,7 +2,9 @@ /* * Copyright (c) 2020 FUJITSU LIMITED. All rights reserved. * Author: Yang Xu - * + */ + +/*\ * Basic test for the BLKRRPART ioctl, it is the same as blockdev * --rereadpt command. */ @@ -22,15 +24,6 @@ static char dev_path[1024]; static int dev_num, attach_flag, dev_fd; static char loop_partpath[1026], sys_loop_partpath[1026]; -static void change_partition(const char *const cmd[]) -{ - int ret; - - ret = tst_cmd(cmd, NULL, NULL, TST_CMD_PASS_RETVAL); - if (ret) - tst_brk(TBROK, "parted return %i", ret); -} - static void check_partition(int part_num, bool value) { int ret; @@ -62,14 +55,14 @@ static void verify_ioctl(void) "mklabel", "msdos", "mkpart", "primary", "ext4", "1M", "10M", NULL}; - const char *const cmd_parted_new[] = {"parted", "-s", "test.img", + const char *const cmd_parted_new[] = {"parted", "-s", dev_path, "mklabel", "msdos", "mkpart", "primary", "ext4", "1M", "10M", "mkpart", "primary", "ext4", "10M", "20M", NULL}; struct loop_info loopinfo = {0}; - change_partition(cmd_parted_old); + SAFE_CMD(cmd_parted_old, NULL, NULL); tst_attach_device(dev_path, "test.img"); attach_flag = 1; @@ -78,12 +71,13 @@ static void verify_ioctl(void) check_partition(1, true); check_partition(2, false); - change_partition(cmd_parted_new); + SAFE_CMD(cmd_parted_new, NULL, NULL); TST_RETRY_FUNC(ioctl(dev_fd, BLKRRPART, 0), TST_RETVAL_EQ0); check_partition(1, true); check_partition(2, true); tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); attach_flag = 0; } @@ -105,6 +99,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .setup = setup, .cleanup = cleanup, .test_all = verify_ioctl, diff --git a/testcases/kernel/syscalls/ioctl/ioctl10.c b/testcases/kernel/syscalls/ioctl/ioctl10.c new file mode 100644 index 00000000..b27e9a84 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl10.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Wei Gao + */ + +/*\ + * [Description] + * + * Test PROCMAP_QUERY ioctl() for /proc/$PID/maps. + * Test base on kernel selftests proc-pid-vm.c. + * + * - ioctl with exact match query_addr + * - ioctl without match query_addr + * - check COVERING_OR_NEXT_VMA query_flags + * - check PROCMAP_QUERY_VMA_WRITABLE query_flags + * - check vma_name_addr content + */ + +#include "config.h" +#include +#include +#include +#include +#include "tst_test.h" +#include "tst_safe_stdio.h" +#include +#include +#include "lapi/ioctl.h" + +#define PROC_MAP_PATH "/proc/self/maps" + +struct map_entry { + unsigned long vm_start; + unsigned long vm_end; + char vm_flags_str[5]; + unsigned long vm_pgoff; + unsigned int vm_major; + unsigned int vm_minor; + unsigned long vm_inode; + char vm_name[256]; + unsigned int vm_flags; +}; + +struct procmap_query *q; +static int fd = -1; + +static unsigned int parse_vm_flags(const char *vm_flags_str) +{ + unsigned int flags = 0; + + if (strchr(vm_flags_str, 'r')) + flags |= PROCMAP_QUERY_VMA_READABLE; + if (strchr(vm_flags_str, 'w')) + flags |= PROCMAP_QUERY_VMA_WRITABLE; + if (strchr(vm_flags_str, 'x')) + flags |= PROCMAP_QUERY_VMA_EXECUTABLE; + if (strchr(vm_flags_str, 's')) + flags |= PROCMAP_QUERY_VMA_SHARED; + + return flags; + +} + +static void parse_maps_file(const char *filename, const char *keyword, struct map_entry *entry) +{ + FILE *fp = SAFE_FOPEN(filename, "r"); + + char line[1024]; + + while (fgets(line, sizeof(line), fp) != NULL) { + if (fnmatch(keyword, line, 0) == 0) { + if (sscanf(line, "%lx-%lx %s %lx %x:%x %lu %s", + &entry->vm_start, &entry->vm_end, entry->vm_flags_str, + &entry->vm_pgoff, &entry->vm_major, &entry->vm_minor, + &entry->vm_inode, entry->vm_name) < 7) + tst_brk(TFAIL, "parse maps file /proc/self/maps failed"); + + entry->vm_flags = parse_vm_flags(entry->vm_flags_str); + + SAFE_FCLOSE(fp); + return; + } + } + + SAFE_FCLOSE(fp); + tst_brk(TFAIL, "parse maps file /proc/self/maps failed"); +} + +static void verify_ioctl(void) +{ + struct map_entry entry; + + memset(&entry, 0, sizeof(entry)); + + parse_maps_file(PROC_MAP_PATH, "*", &entry); + + /* CASE 1: exact MATCH at query_addr */ + memset(q, 0, sizeof(*q)); + q->size = sizeof(*q); + q->query_addr = (uint64_t)entry.vm_start; + q->query_flags = 0; + + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, q)); + + TST_EXP_EQ_LU(q->query_addr, entry.vm_start); + TST_EXP_EQ_LU(q->query_flags, 0); + TST_EXP_EQ_LU(q->vma_flags, entry.vm_flags); + TST_EXP_EQ_LU(q->vma_start, entry.vm_start); + TST_EXP_EQ_LU(q->vma_end, entry.vm_end); + TST_EXP_EQ_LU(q->vma_page_size, getpagesize()); + TST_EXP_EQ_LU(q->vma_offset, entry.vm_pgoff); + TST_EXP_EQ_LU(q->inode, entry.vm_inode); + TST_EXP_EQ_LU(q->dev_major, entry.vm_major); + TST_EXP_EQ_LU(q->dev_minor, entry.vm_minor); + + /* CASE 2: NO MATCH at query_addr */ + memset(q, 0, sizeof(*q)); + q->size = sizeof(*q); + q->query_addr = entry.vm_start - 1; + q->query_flags = 0; + + TST_EXP_FAIL(ioctl(fd, PROCMAP_QUERY, q), ENOENT); + + /* CASE 3: MATCH COVERING_OR_NEXT_VMA */ + memset(q, 0, sizeof(*q)); + q->size = sizeof(*q); + q->query_addr = entry.vm_start - 1; + q->query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA; + + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, q)); + + /* CASE 4: NO MATCH WRITABLE at query_addr */ + memset(&entry, 0, sizeof(entry)); + parse_maps_file(PROC_MAP_PATH, "*r-?p *", &entry); + + memset(q, 0, sizeof(*q)); + q->size = sizeof(*q); + q->query_addr = entry.vm_start; + q->query_flags = PROCMAP_QUERY_VMA_WRITABLE; + TST_EXP_FAIL(ioctl(fd, PROCMAP_QUERY, q), ENOENT); + + /* CASE 5: check vma_name_addr content */ + char process_name[256]; + char pattern[258]; + char buf[256]; + + SAFE_READLINK("/proc/self/exe", process_name, sizeof(process_name)); + snprintf(pattern, sizeof(pattern), "*%s*", process_name); + memset(&entry, 0, sizeof(entry)); + parse_maps_file(PROC_MAP_PATH, pattern, &entry); + + memset(q, 0, sizeof(*q)); + q->size = sizeof(*q); + q->query_addr = entry.vm_start; + q->query_flags = 0; + q->vma_name_addr = (uint64_t)(unsigned long)buf; + q->vma_name_size = sizeof(buf); + + TST_EXP_PASS(ioctl(fd, PROCMAP_QUERY, q)); + TST_EXP_EQ_LU(q->vma_name_size, strlen(process_name) + 1); + TST_EXP_EQ_STR((char *)q->vma_name_addr, process_name); + + SAFE_CLOSE(fd); +} + +static void setup(void) +{ + struct procmap_query q = {}; + + fd = SAFE_OPEN(PROC_MAP_PATH, O_RDONLY); + + if (tst_kvercmp(6, 11, 0) < 0) { + TEST(ioctl(fd, PROCMAP_QUERY, q)); + + if ((TST_RET == -1) && (TST_ERR == ENOTTY)) + tst_brk(TCONF, + "This system does not provide support for ioctl(PROCMAP_QUERY)"); + } +} + +static void cleanup(void) +{ + if (fd != -1) + SAFE_CLOSE(fd); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = verify_ioctl, + .bufs = (struct tst_buffers []) { + {&q, .size = sizeof(*q)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ficlone01.c b/testcases/kernel/syscalls/ioctl/ioctl_ficlone01.c new file mode 100644 index 00000000..a30fa922 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_ficlone01.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com + */ + +/*\ + * This test verifies that ioctl() FICLONE feature clones file content from + * one file to an another. + * + * [Algorithm] + * + * - populate source file + * - clone source content inside destination file + * - verify that source content has been cloned inside destination file + * - write a single byte inside destination file + * - verify that source content didn't change while destination did + */ + +#include "tst_test.h" +#include "lapi/ficlone.h" + +#define MNTPOINT "mnt" +#define SRCPATH MNTPOINT "/file0" +#define DSTPATH MNTPOINT "/file1" + +#define FILEDATA "qwerty" +#define FILESIZE sizeof(FILEDATA) + +static int src_fd = -1; +static int dst_fd = -1; + +static void run(void) +{ + char buff[FILESIZE]; + struct stat src_stat; + struct stat dst_stat; + + src_fd = SAFE_OPEN(SRCPATH, O_CREAT | O_RDWR, 0640); + dst_fd = SAFE_OPEN(DSTPATH, O_CREAT | O_RDWR, 0640); + + tst_res(TINFO, "Writing data inside src file"); + + SAFE_WRITE(1, src_fd, FILEDATA, FILESIZE); + SAFE_FSYNC(src_fd); + + TST_EXP_PASS(ioctl(dst_fd, FICLONE, src_fd)); + if (TST_RET == -1) + return; + + SAFE_FSYNC(dst_fd); + + tst_res(TINFO, "Verifing that data is cloned between files"); + + SAFE_FSTAT(src_fd, &src_stat); + SAFE_FSTAT(dst_fd, &dst_stat); + + TST_EXP_EXPR(src_stat.st_ino != dst_stat.st_ino, + "inode is different. %lu != %lu", + src_stat.st_ino, + dst_stat.st_ino); + + TST_EXP_EQ_LI(src_stat.st_size, dst_stat.st_size); + + SAFE_READ(0, dst_fd, buff, FILESIZE); + + TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE), + "dst file has the src file content (\"%s\" - %ld bytes)", + buff, + FILESIZE); + + tst_res(TINFO, "Writing a byte inside dst file"); + + SAFE_LSEEK(dst_fd, 0, SEEK_SET); + SAFE_WRITE(SAFE_WRITE_ALL, dst_fd, "+", 1); + SAFE_FSYNC(dst_fd); + + tst_res(TINFO, "Verifing that src file content didn't change"); + + SAFE_FSTAT(src_fd, &src_stat); + SAFE_FSTAT(dst_fd, &dst_stat); + + TST_EXP_EQ_LI(dst_stat.st_size, src_stat.st_size); + + SAFE_READ(0, src_fd, buff, FILESIZE); + + TST_EXP_EXPR(!strncmp(buff, FILEDATA, FILESIZE), + "src file content didn't change"); + + SAFE_CLOSE(src_fd); + SAFE_CLOSE(dst_fd); + + SAFE_UNLINK(SRCPATH); + SAFE_UNLINK(DSTPATH); +} + +static void cleanup(void) +{ + if (src_fd != -1) + SAFE_CLOSE(src_fd); + + if (dst_fd != -1) + SAFE_CLOSE(dst_fd); +} + +static struct tst_test test = { + .test_all = run, + .cleanup = cleanup, + .min_kver = "4.5", + .needs_root = 1, + .mount_device = 1, + .mntpoint = MNTPOINT, + .filesystems = (struct tst_fs []) { + {.type = "btrfs"}, + {.type = "bcachefs"}, + { + .type = "xfs", + .min_kver = "4.16", + .mkfs_ver = "mkfs.xfs >= 1.5.0", + .mkfs_opts = (const char *const []) {"-m", "reflink=1", NULL}, + }, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ficlone02.c b/testcases/kernel/syscalls/ioctl/ioctl_ficlone02.c new file mode 100644 index 00000000..76a6bb8a --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_ficlone02.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com + */ + +/*\ + * This test verifies that ioctl() FICLONE/FICLONERANGE feature correctly raises + * EOPNOTSUPP when an unsupported filesystem is used. In particular, filesystems + * which don't support copy-on-write. + */ + +#include "tst_test.h" +#include "lapi/ficlone.h" + +#define MNTPOINT "mnt" +#define SRCPATH MNTPOINT "/file0" +#define DSTPATH MNTPOINT "/file1" + +static struct file_clone_range *clone_range; + +static void run(void) +{ + int src_fd; + int dst_fd; + + src_fd = SAFE_OPEN(SRCPATH, O_CREAT | O_RDWR, 0640); + dst_fd = SAFE_OPEN(DSTPATH, O_CREAT | O_RDWR, 0640); + + clone_range->src_fd = src_fd; + + TST_EXP_FAIL(ioctl(dst_fd, FICLONE, src_fd), EOPNOTSUPP); + TST_EXP_FAIL(ioctl(dst_fd, FICLONERANGE, clone_range), EOPNOTSUPP); + + SAFE_CLOSE(src_fd); + SAFE_CLOSE(dst_fd); +} + +static void setup(void) +{ + struct stat sb; + + SAFE_STAT(MNTPOINT, &sb); + + tst_fill_file(SRCPATH, 0x00, sb.st_blksize, 1); + + clone_range->src_offset = 0; + clone_range->src_length = sb.st_blksize; + clone_range->dest_offset = 0; +} + +static struct tst_test test = { + .timeout = 10, + .test_all = run, + .setup = setup, + .min_kver = "4.5", + .needs_root = 1, + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .skip_filesystems = (const char *[]) { + "bcachefs", + "btrfs", + "overlayfs", + "nfs", + "xfs", + NULL, + }, + .bufs = (struct tst_buffers []) { + {&clone_range, .size = sizeof(struct file_clone_range)}, + {}, + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ficlone03.c b/testcases/kernel/syscalls/ioctl/ioctl_ficlone03.c new file mode 100644 index 00000000..6a9d270d --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_ficlone03.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com + */ + +/*\ + * This test verifies that ioctl() FICLONE/FICLONERANGE feature correctly raises + * exceptions when it's supposed to. + */ + +#include "tst_test.h" +#include "lapi/ficlone.h" + +#define MNTPOINT "mnt" + +static struct file_clone_range *clone_range; + +static int invalid_fd = -1; +static int rw_file = -1; +static int ro_file = -1; +static int wo_file = -1; +static int dir_fd = -1; +static int immut_fd = -1; +static int mnt_file = -1; + +static struct tcase { + int *src_fd; + int *dst_fd; + int errno_exp; + char *msg; +} tcases[] = { + {&invalid_fd, &rw_file, EBADF, "invalid source"}, + {&rw_file, &invalid_fd, EBADF, "invalid destination"}, + {&rw_file, &ro_file, EBADF, "read-only destination"}, + {&wo_file, &rw_file, EBADF, "write-only source"}, + {&rw_file, &dir_fd, EISDIR, "source is a directory"}, + {&dir_fd, &rw_file, EISDIR, "destination is a directory"}, + {&mnt_file, &immut_fd, EPERM, "destination is immutable"}, + {&rw_file, &mnt_file, EXDEV, "destination is on a different mount"}, + {&mnt_file, &rw_file, EXDEV, "source is on a different mount"}, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + TST_EXP_FAIL(ioctl(*tc->dst_fd, FICLONE, *tc->src_fd), + tc->errno_exp, + "%s", tc->msg); + + clone_range->src_fd = *tc->src_fd; + + TST_EXP_FAIL(ioctl(*tc->dst_fd, FICLONERANGE, clone_range), + tc->errno_exp, + "%s", tc->msg); +} + +static void setup(void) +{ + int attr; + struct stat sb; + + rw_file = SAFE_OPEN("ok_only", O_CREAT | O_RDWR, 0640); + ro_file = SAFE_OPEN("rd_only", O_CREAT | O_RDONLY, 0640); + wo_file = SAFE_OPEN("rw_only", O_CREAT | O_WRONLY, 0640); + + if (access("mydir", F_OK) == -1) + SAFE_MKDIR("mydir", 0640); + + dir_fd = SAFE_OPEN("mydir", O_DIRECTORY, 0640); + + attr = FS_IMMUTABLE_FL; + immut_fd = SAFE_OPEN(MNTPOINT"/immutable", O_CREAT | O_RDWR, 0640); + SAFE_IOCTL(immut_fd, FS_IOC_SETFLAGS, &attr); + + mnt_file = SAFE_OPEN(MNTPOINT"/file", O_CREAT | O_RDWR, 0640); + + SAFE_STAT(MNTPOINT, &sb); + + clone_range->src_offset = 0; + clone_range->src_length = sb.st_blksize; + clone_range->dest_offset = 0; +} + +static void cleanup(void) +{ + int attr; + + SAFE_IOCTL(immut_fd, FS_IOC_GETFLAGS, &attr); + attr &= ~FS_IMMUTABLE_FL; + SAFE_IOCTL(immut_fd, FS_IOC_SETFLAGS, &attr); + SAFE_CLOSE(immut_fd); + + SAFE_CLOSE(rw_file); + SAFE_CLOSE(ro_file); + SAFE_CLOSE(wo_file); + SAFE_CLOSE(dir_fd); + SAFE_CLOSE(mnt_file); +} + +static struct tst_test test = { + .timeout = 1, + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .min_kver = "4.5", + .needs_root = 1, + .mount_device = 1, + .mntpoint = MNTPOINT, + .filesystems = (struct tst_fs []) { + {.type = "btrfs"}, + {.type = "bcachefs"}, + { + .type = "xfs", + .min_kver = "4.16", + .mkfs_ver = "mkfs.xfs >= 1.5.0", + .mkfs_opts = (const char *const []) {"-m", "reflink=1", NULL}, + }, + {} + }, + .bufs = (struct tst_buffers []) { + {&clone_range, .size = sizeof(struct file_clone_range)}, + {}, + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ficlone04.c b/testcases/kernel/syscalls/ioctl/ioctl_ficlone04.c new file mode 100644 index 00000000..96f9d583 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_ficlone04.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Andrea Cervesato + */ + +/*\ + * This test verifies that ioctl() FICLONE/FICLONERANGE feature raises the right + * error according with bad file descriptors. + */ + +#include "tst_test.h" +#include "lapi/ficlone.h" + +static void test_bad_fd(struct tst_fd *fd_src, struct tst_fd *fd_dst) +{ + if (fd_src->type == TST_FD_FILE && fd_src->type == fd_dst->type) { + tst_res(TINFO, "Skipping file: SUCCESS"); + return; + } + + int exp_errnos[] = { + EOPNOTSUPP, + EPERM, + EISDIR, + EBADF, + EINVAL, + EXDEV, + }; + + TST_EXP_FAIL2_ARR(ioctl(fd_dst->fd, FICLONE, fd_src->fd), + exp_errnos, ARRAY_SIZE(exp_errnos), + "ioctl(%s, FICLONE, %s)", + tst_fd_desc(fd_src), + tst_fd_desc(fd_dst)); +} + +static void run(void) +{ + TST_FD_FOREACH(fd_src) { + TST_FD_FOREACH(fd_dst) + test_bad_fd(&fd_src, &fd_dst); + } +} + +static struct tst_test test = { + .test_all = run, + .min_kver = "4.5", + .needs_root = 1, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ficlonerange01.c b/testcases/kernel/syscalls/ioctl/ioctl_ficlonerange01.c new file mode 100644 index 00000000..5b51f1d1 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_ficlonerange01.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com + */ + +/*\ + * This test verifies that ioctl() FICLONERANGE feature clones file content from + * one file to an another. + * + * [Algorithm] + * + * - populate source file + * - clone a portion of source content inside destination file + * - verify that source content portion has been cloned inside destination file + * - write a single byte inside destination file + * - verify that source content didn't change while destination did + */ + +#include "tst_test.h" +#include "lapi/ficlone.h" + +#define MNTPOINT "mnt" +#define SRCPATH MNTPOINT "/file0" +#define DSTPATH MNTPOINT "/file1" +#define CHUNKS 64 + +static struct file_clone_range *clone_range; +static int filesize; +static int offset; +static int leftsize; +static int src_fd = -1; +static int dst_fd = -1; +static char *data; +static char *buff; + +static void run(void) +{ + struct stat src_stat; + struct stat dst_stat; + + for (int i = 0; i < filesize; i++) + data[i] = 'a' + (rand() % 21); + + src_fd = SAFE_OPEN(SRCPATH, O_CREAT | O_RDWR, 0640); + dst_fd = SAFE_OPEN(DSTPATH, O_CREAT | O_RDWR, 0640); + + tst_res(TINFO, "Writing data inside src file"); + + SAFE_WRITE(SAFE_WRITE_ALL, src_fd, data, filesize); + SAFE_FSYNC(src_fd); + + clone_range->src_fd = src_fd; + TST_EXP_PASS(ioctl(dst_fd, FICLONERANGE, clone_range)); + if (TST_RET == -1) + return; + + SAFE_FSYNC(dst_fd); + + tst_res(TINFO, "Verifing that data is cloned between files"); + + SAFE_FSTAT(src_fd, &src_stat); + SAFE_FSTAT(dst_fd, &dst_stat); + + TST_EXP_EXPR(src_stat.st_ino != dst_stat.st_ino, + "inode is different. %lu != %lu", + src_stat.st_ino, + dst_stat.st_ino); + + TST_EXP_EQ_LI(src_stat.st_size, filesize); + TST_EXP_EQ_LI(dst_stat.st_size, leftsize); + + SAFE_READ(0, dst_fd, buff, leftsize); + + TST_EXP_EXPR(!strncmp(buff, data + offset, leftsize), + "dst file has been cloned (%d bytes)", + leftsize); + + tst_res(TINFO, "Writing a byte inside dst file"); + + SAFE_LSEEK(dst_fd, 0, SEEK_SET); + SAFE_WRITE(SAFE_WRITE_ALL, dst_fd, "!", 1); + SAFE_FSYNC(dst_fd); + + tst_res(TINFO, "Verifing that src file content didn't change"); + + SAFE_FSTAT(src_fd, &src_stat); + SAFE_FSTAT(dst_fd, &dst_stat); + + TST_EXP_EQ_LI(src_stat.st_size, filesize); + TST_EXP_EQ_LI(dst_stat.st_size, leftsize); + + SAFE_LSEEK(src_fd, 0, SEEK_SET); + SAFE_READ(0, src_fd, buff, filesize); + + TST_EXP_EXPR(!strncmp(buff, data, filesize), + "src file content didn't change"); + + SAFE_CLOSE(src_fd); + SAFE_CLOSE(dst_fd); + + SAFE_UNLINK(SRCPATH); + SAFE_UNLINK(DSTPATH); +} + +static void setup(void) +{ + struct stat sb; + + SAFE_STAT(MNTPOINT, &sb); + + filesize = sb.st_blksize * CHUNKS; + offset = filesize / 4; + leftsize = filesize - offset; + + clone_range->src_offset = offset; + clone_range->src_length = leftsize; + clone_range->dest_offset = 0; + + data = SAFE_MALLOC(filesize); + buff = SAFE_MALLOC(filesize); + + srand(time(NULL)); +} + +static void cleanup(void) +{ + free(buff); + free(data); + + if (src_fd != -1) + SAFE_CLOSE(src_fd); + + if (dst_fd != -1) + SAFE_CLOSE(dst_fd); +} + +static struct tst_test test = { + .timeout = 1, + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .min_kver = "4.5", + .needs_root = 1, + .mount_device = 1, + .mntpoint = MNTPOINT, + .filesystems = (struct tst_fs []) { + {.type = "btrfs"}, + {.type = "bcachefs"}, + { + .type = "xfs", + .min_kver = "4.16", + .mkfs_ver = "mkfs.xfs >= 1.5.0", + .mkfs_opts = (const char *const []) {"-m", "reflink=1", NULL}, + }, + {} + }, + .bufs = (struct tst_buffers []) { + {&clone_range, .size = sizeof(struct file_clone_range)}, + {}, + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ficlonerange02.c b/testcases/kernel/syscalls/ioctl/ioctl_ficlonerange02.c new file mode 100644 index 00000000..6c93d8dc --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_ficlonerange02.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com + */ + +/*\ + * This test verifies that ioctl() FICLONERANGE feature correctly raises + * EINVAL when: + * - filesystem does not support overlapping reflink ranges in the same file + * - filesystem does not support reflinking on bad blocks alignment + */ + +#include "tst_test.h" +#include "lapi/ficlone.h" + +#define MNTPOINT "mnt" +#define SRCPATH MNTPOINT "/file0" +#define DSTPATH MNTPOINT "/file1" +#define CHUNKS 10 + +static struct file_clone_range *clone_range; +static int filesize; +static int alignment; +static char *data; + +static void run(void) +{ + int src_fd; + int dst_fd; + + src_fd = SAFE_OPEN(SRCPATH, O_CREAT | O_RDWR, 0640); + SAFE_WRITE(SAFE_WRITE_ALL, src_fd, data, filesize); + SAFE_FSYNC(src_fd); + + dst_fd = SAFE_OPEN(DSTPATH, O_CREAT | O_RDWR, 0640); + + clone_range->src_fd = dst_fd; + clone_range->src_offset = 0; + clone_range->src_length = filesize; + clone_range->dest_offset = 0; + + TST_EXP_FAIL(ioctl(dst_fd, FICLONERANGE, clone_range), EINVAL, + "overlapping reflink ranges in the same file"); + + clone_range->src_fd = src_fd; + clone_range->src_offset = 0; + clone_range->src_length = alignment - 1; + clone_range->dest_offset = 0; + + TST_EXP_FAIL(ioctl(dst_fd, FICLONERANGE, clone_range), EINVAL, + "bad blocks alignment"); + + SAFE_CLOSE(src_fd); + SAFE_CLOSE(dst_fd); +} + +static void setup(void) +{ + struct stat sb; + + SAFE_STAT(MNTPOINT, &sb); + + alignment = sb.st_blksize; + filesize = alignment * CHUNKS; + + data = SAFE_MALLOC(filesize); +} + +static void cleanup(void) +{ + free(data); +} + +static struct tst_test test = { + .timeout = 4, + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .min_kver = "4.5", + .needs_root = 1, + .mount_device = 1, + .mntpoint = MNTPOINT, + .filesystems = (struct tst_fs []) { + {.type = "btrfs"}, + {.type = "bcachefs"}, + { + .type = "xfs", + .min_kver = "4.16", + .mkfs_ver = "mkfs.xfs >= 1.5.0", + .mkfs_opts = (const char *const []) {"-m", "reflink=1", NULL}, + }, + {} + }, + .bufs = (struct tst_buffers []) { + {&clone_range, .size = sizeof(struct file_clone_range)}, + {}, + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_fiemap01.c b/testcases/kernel/syscalls/ioctl/ioctl_fiemap01.c new file mode 100644 index 00000000..31187b19 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_fiemap01.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Wei Gao + */ + +/*\ + * Verify basic fiemap ioctl functionality, including: + * + * - The ioctl returns EBADR if it receives invalid fm_flags. + * - 0 extents are reported for an empty file. + * - The ioctl correctly retrieves single and multiple extent mappings after writing to the file. + */ + +#include +#include +#include +#include + +#include "tst_test.h" + +#define MNTPOINT "mntpoint" +#define TESTFILE "testfile" +#define NUM_EXTENT 3 + +static void print_extens(struct fiemap *fiemap) +{ + tst_res(TDEBUG, "File extent count: %u", fiemap->fm_mapped_extents); + + for (unsigned int i = 0; i < fiemap->fm_mapped_extents; ++i) { + tst_res(TDEBUG, "Extent %u: Logical offset: %llu, Physical offset: %llu, flags: %u, Length: %llu", + i + 1, + fiemap->fm_extents[i].fe_logical, + fiemap->fm_extents[i].fe_physical, + fiemap->fm_extents[i].fe_flags, + fiemap->fm_extents[i].fe_length); + } +} + +static void check_extent_count(struct fiemap *fiemap, unsigned int fm_mapped_extents) +{ + TST_EXP_EXPR(fiemap->fm_mapped_extents == fm_mapped_extents, + "extent fm_mapped_extents is %d", fm_mapped_extents); +} + +static void check_extent(struct fiemap *fiemap, int index_extents, unsigned int fe_mask, + unsigned int fe_flags, unsigned int fe_length) +{ + struct fiemap_extent *extent = &fiemap->fm_extents[index_extents]; + + TST_EXP_EQ_LU((extent->fe_flags & fe_mask), fe_flags); + TST_EXP_EXPR(extent->fe_physical >= 1, "fe_physical > %d", 1); + TST_EXP_EQ_LU(extent->fe_length, fe_length); +} + +static void verify_ioctl(void) +{ + int fd; + struct fiemap *fiemap; + struct statvfs fs_info; + unsigned long blk_size; + + fd = SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT, 0644); + + SAFE_STATVFS(".", &fs_info); + + blk_size = fs_info.f_bsize; + + fiemap = SAFE_MALLOC(sizeof(struct fiemap) + sizeof(struct fiemap_extent) * NUM_EXTENT); + fiemap->fm_start = 0; + fiemap->fm_length = ~0ULL; + fiemap->fm_extent_count = 1; + + fiemap->fm_flags = -1; + TST_EXP_FAIL(ioctl(fd, FS_IOC_FIEMAP, fiemap), EBADR); + + fiemap->fm_flags = 0; + TST_EXP_PASS(ioctl(fd, FS_IOC_FIEMAP, fiemap)); + print_extens(fiemap); + TST_EXP_EXPR(fiemap->fm_mapped_extents == 0, + "Empty file should have 0 extends mapped"); + + char *buf = SAFE_MALLOC(blk_size); + + SAFE_WRITE(SAFE_WRITE_ANY, fd, buf, blk_size); + fiemap->fm_flags = FIEMAP_FLAG_SYNC; + TST_EXP_PASS(ioctl(fd, FS_IOC_FIEMAP, fiemap)); + print_extens(fiemap); + check_extent_count(fiemap, 1); + check_extent(fiemap, 0, FIEMAP_EXTENT_LAST, FIEMAP_EXTENT_LAST, blk_size); + + fiemap->fm_extent_count = NUM_EXTENT; + SAFE_LSEEK(fd, 2 * blk_size, SEEK_SET); + SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, blk_size); + SAFE_LSEEK(fd, 4 * blk_size, SEEK_SET); + SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, blk_size); + TST_EXP_PASS(ioctl(fd, FS_IOC_FIEMAP, fiemap)); + print_extens(fiemap); + check_extent_count(fiemap, NUM_EXTENT); + + for (int i = 0; i < NUM_EXTENT - 1; i++) + check_extent(fiemap, i, FIEMAP_EXTENT_LAST, 0, blk_size); + + check_extent(fiemap, NUM_EXTENT - 1, FIEMAP_EXTENT_LAST, FIEMAP_EXTENT_LAST, blk_size); + + free(buf); + free(fiemap); + SAFE_CLOSE(fd); + SAFE_UNLINK(TESTFILE); +} + +static void setup(void) +{ + SAFE_CHDIR(MNTPOINT); +} + +static struct tst_test test = { + .setup = setup, + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .skip_filesystems = (const char *const[]) { + "exfat", "vfat", "fuse", "ntfs", "tmpfs", NULL + }, + .test_all = verify_ioctl, + .needs_root = 1, +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_loop01.c b/testcases/kernel/syscalls/ioctl/ioctl_loop01.c index 734d803d..cb1b506d 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_loop01.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_loop01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests ioctl() on loopdevice with LO_FLAGS_AUTOCLEAR and LO_FLAGS_PARTSCAN flags. * * For LO_FLAGS_AUTOCLEAR flag, only checks autoclear field value in sysfs @@ -27,7 +25,8 @@ #include "lapi/loop.h" #include "tst_test.h" -static char dev_path[1024], backing_path[1024], backing_file_path[1024]; +static char dev_path[1024], backing_path[1024]; +static char *backing_file_path; static int dev_num, attach_flag, dev_fd, parted_sup; /* @@ -60,7 +59,7 @@ static void check_loop_value(int set_flag, int get_flag, int autoclear_field) TST_ASSERT_INT(autoclear_path, autoclear_field); if (!parted_sup) { - tst_res(TINFO, "Current environment doesn't have parted disk, skip it"); + tst_res(TCONF, "Current environment doesn't have parted disk, skip it"); return; } @@ -79,7 +78,21 @@ static void check_loop_value(int set_flag, int get_flag, int autoclear_field) static void verify_ioctl_loop(void) { + int ret; + const char *const cmd_parted[] = {"parted", "-s", dev_path, "mklabel", "msdos", "mkpart", + "primary", "ext4", "1M", "10M", NULL}; + + tst_fill_file("test.img", 0, 1024 * 1024, 10); tst_attach_device(dev_path, "test.img"); + + ret = tst_cmd(cmd_parted, NULL, NULL, TST_CMD_PASS_RETVAL); + if (!ret) + parted_sup = 1; + else if (ret == 255) + tst_res(TCONF, "parted binary not installed or failed"); + else + tst_res(TCONF, "parted exited with %i", ret); + attach_flag = 1; TST_ASSERT_INT(partscan_path, 0); @@ -92,39 +105,22 @@ static void verify_ioctl_loop(void) check_loop_value(0, LO_FLAGS_PARTSCAN, 0); tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); + attach_flag = 0; } static void setup(void) { - int ret; - const char *const cmd_parted[] = {"parted", "-s", "test.img", "mklabel", "msdos", "mkpart", - "primary", "ext4", "1M", "10M", NULL}; - dev_num = tst_find_free_loopdev(dev_path, sizeof(dev_path)); if (dev_num < 0) tst_brk(TBROK, "Failed to find free loop device"); - tst_fill_file("test.img", 0, 1024 * 1024, 10); - - ret = tst_cmd(cmd_parted, NULL, NULL, TST_CMD_PASS_RETVAL); - switch (ret) { - case 0: - parted_sup = 1; - break; - case 255: - tst_res(TCONF, "parted binary not installed or failed"); - break; - default: - tst_res(TCONF, "parted exited with %i", ret); - break; - } - sprintf(partscan_path, "/sys/block/loop%d/loop/partscan", dev_num); sprintf(autoclear_path, "/sys/block/loop%d/loop/autoclear", dev_num); sprintf(backing_path, "/sys/block/loop%d/loop/backing_file", dev_num); sprintf(sys_loop_partpath, "/sys/block/loop%d/loop%dp1", dev_num, dev_num); - sprintf(backing_file_path, "%s/test.img", tst_get_tmpdir()); + backing_file_path = tst_tmpdir_genpath("test.img"); sprintf(loop_partpath, "%sp1", dev_path); dev_fd = SAFE_OPEN(dev_path, O_RDWR); } @@ -138,6 +134,7 @@ static void cleanup(void) } static struct tst_test test = { + .timeout = 1, .setup = setup, .cleanup = cleanup, .test_all = verify_ioctl_loop, diff --git a/testcases/kernel/syscalls/ioctl/ioctl_loop02.c b/testcases/kernel/syscalls/ioctl/ioctl_loop02.c index 12d4e823..6026af1e 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_loop02.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_loop02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests ioctl() on loopdevice with LO_FLAGS_READ_ONLY (similar as losetup -r) and * LOOP_CHANGE_FD flags. * @@ -27,7 +25,9 @@ #include "tst_test.h" static int file_fd, file_change_fd, file_fd_invalid; -static char backing_path[1024], backing_file_path[1024], backing_file_change_path[1024]; +static char backing_path[1024]; +static char *backing_file_path; +static char *backing_file_change_path; static int attach_flag, dev_fd, loop_configure_sup = 1; static char loop_ro_path[1024], dev_path[1024]; static struct loop_config loopconfig; @@ -101,6 +101,7 @@ static void verify_ioctl_loop(unsigned int n) SAFE_CLOSE(file_fd); tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); attach_flag = 0; } @@ -109,7 +110,6 @@ static void setup(void) int dev_num; int ret; - char *tmpdir = tst_get_tmpdir(); dev_num = tst_find_free_loopdev(dev_path, sizeof(dev_path)); if (dev_num < 0) tst_brk(TBROK, "Failed to find free loop device"); @@ -119,12 +119,10 @@ static void setup(void) tst_fill_file("test2.img", 0, 2048, 20); sprintf(backing_path, "/sys/block/loop%d/loop/backing_file", dev_num); - sprintf(backing_file_path, "%s/test.img", tmpdir); - sprintf(backing_file_change_path, "%s/test1.img", tmpdir); + backing_file_path = tst_tmpdir_genpath("test.img"); + backing_file_change_path = tst_tmpdir_genpath("test1.img"); sprintf(loop_ro_path, "/sys/block/loop%d/ro", dev_num); - free(tmpdir); - file_change_fd = SAFE_OPEN("test1.img", O_RDWR); file_fd_invalid = SAFE_OPEN("test2.img", O_RDWR); diff --git a/testcases/kernel/syscalls/ioctl/ioctl_loop03.c b/testcases/kernel/syscalls/ioctl/ioctl_loop03.c index a7b0230e..dc39dbbb 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_loop03.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_loop03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests ioctl() on loopdevice with LOOP_CHANGE_FD flag. * * Tests whether LOOP_CHANGE_FD can not succeed (get EINVAL error) diff --git a/testcases/kernel/syscalls/ioctl/ioctl_loop04.c b/testcases/kernel/syscalls/ioctl/ioctl_loop04.c index 5b7506ea..839648f2 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_loop04.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_loop04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests ioctl() on loopdevice with LOOP_SET_CAPACITY flag. * * Tests whether LOOP_SET_CAPACITY can update a live @@ -63,6 +61,7 @@ static void verify_ioctl_loop(void) SAFE_CLOSE(file_fd); tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); unlink("test.img"); attach_flag = 0; } diff --git a/testcases/kernel/syscalls/ioctl/ioctl_loop05.c b/testcases/kernel/syscalls/ioctl/ioctl_loop05.c index 3a5d5afe..0ced7eda 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_loop05.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_loop05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests ioctl() on loopdevice with LOOP_SET_DIRECT_IO flag. * * Tests whether LOOP_SET_DIRECT_IO can update a live loop device dio mode. @@ -37,7 +35,8 @@ #define DIO_MESSAGE "In dio mode" #define NON_DIO_MESSAGE "In non dio mode" -static char dev_path[1024], sys_loop_diopath[1024], backing_file_path[1024]; +static char dev_path[1024], sys_loop_diopath[1024]; +static char *backing_file_path; static int dev_num, dev_fd, block_devfd, attach_flag, logical_block_size; static void check_dio_value(int flag) @@ -124,7 +123,7 @@ static void setup(void) * size of loop is bigger than the backing device's and the loop * needn't transform transfer. */ - sprintf(backing_file_path, "%s/test.img", tst_get_tmpdir()); + backing_file_path = tst_tmpdir_genpath("test.img"); tst_find_backing_dev(backing_file_path, bd_path, sizeof(bd_path)); block_devfd = SAFE_OPEN(bd_path, O_RDWR); SAFE_IOCTL(block_devfd, BLKSSZGET, &logical_block_size); diff --git a/testcases/kernel/syscalls/ioctl/ioctl_loop06.c b/testcases/kernel/syscalls/ioctl/ioctl_loop06.c index 6d009af6..35e9e79e 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_loop06.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_loop06.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests invalid block size of loopdevice by using ioctl() with * LOOP_SET_BLOCK_SIZE and LOOP_CONFIGURE flags. */ @@ -16,7 +14,9 @@ #include #include #include +#include "lapi/blkdev.h" #include "lapi/loop.h" +#include "tst_fs.h" #include "tst_test.h" static char dev_path[1024]; @@ -33,7 +33,7 @@ static struct tcase { "Using LOOP_SET_BLOCK_SIZE with arg < 512"}, {&invalid_value, LOOP_SET_BLOCK_SIZE, - "Using LOOP_SET_BLOCK_SIZE with arg > PAGE_SIZE"}, + "Using LOOP_SET_BLOCK_SIZE with arg > BLK_MAX_BLOCK_SIZE"}, {&unalign_value, LOOP_SET_BLOCK_SIZE, "Using LOOP_SET_BLOCK_SIZE with arg != power_of_2"}, @@ -42,7 +42,7 @@ static struct tcase { "Using LOOP_CONFIGURE with block_size < 512"}, {&invalid_value, LOOP_CONFIGURE, - "Using LOOP_CONFIGURE with block_size > PAGE_SIZE"}, + "Using LOOP_CONFIGURE with block_size > BLK_MAX_BLOCK_SIZE"}, {&unalign_value, LOOP_CONFIGURE, "Using LOOP_CONFIGURE with block_size != power_of_2"}, @@ -57,8 +57,10 @@ static void verify_ioctl_loop(unsigned int n) if (TST_RET == 0) { tst_res(TFAIL, "Set block size succeed unexpectedly"); - if (tcases[n].ioctl_flag == LOOP_CONFIGURE) + if (tcases[n].ioctl_flag == LOOP_CONFIGURE) { tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); + } return; } if (TST_ERR == EINVAL) @@ -87,6 +89,7 @@ static void run(unsigned int n) } if (attach_flag) { tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); attach_flag = 0; } loopconfig.block_size = *(tc->setvalue); @@ -102,15 +105,18 @@ static void setup(void) if (dev_num < 0) tst_brk(TBROK, "Failed to find free loop device"); - tst_fill_file("test.img", 0, 1024, 1024); + size_t bs = (BLK_MAX_BLOCK_SIZE < TST_MB) ? 1024 : 4 * BLK_MAX_BLOCK_SIZE / 1024; + tst_fill_file("test.img", 0, bs, 1024); + half_value = 256; pg_size = getpagesize(); - invalid_value = pg_size * 2 ; + invalid_value = BLK_MAX_BLOCK_SIZE * 2; unalign_value = pg_size - 1; dev_fd = SAFE_OPEN(dev_path, O_RDWR); + ret = ioctl(dev_fd, LOOP_SET_BLOCK_SIZE, 512); - if (ioctl(dev_fd, LOOP_SET_BLOCK_SIZE, 512) && errno == EINVAL) + if (ret && (errno == EINVAL || errno == ENOTTY)) tst_brk(TCONF, "LOOP_SET_BLOCK_SIZE is not supported"); file_fd = SAFE_OPEN("test.img", O_RDWR); diff --git a/testcases/kernel/syscalls/ioctl/ioctl_loop07.c b/testcases/kernel/syscalls/ioctl/ioctl_loop07.c index d44f3621..be136fe0 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_loop07.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_loop07.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests ioctl() on loopdevice with LOOP_SET_STATUS64 and LOOP_GET_STATUS64 flags. * * Tests lo_sizelimit field. If lo_sizelimit is 0, it means max @@ -73,6 +71,7 @@ static void verify_ioctl_loop(unsigned int n) /*Reset*/ if (tc->ioctl_flag == LOOP_CONFIGURE) { tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); } else { loopinfo.lo_sizelimit = 0; TST_RETRY_FUNC(ioctl(dev_fd, LOOP_SET_STATUS, &loopinfo), TST_RETVAL_EQ0); @@ -101,6 +100,7 @@ static void run(unsigned int n) } if (attach_flag) { tst_detach_device_by_fd(dev_path, dev_fd); + dev_fd = SAFE_OPEN(dev_path, O_RDWR); attach_flag = 0; } loopconfig.info.lo_sizelimit = tc->set_sizelimit; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ns01.c b/testcases/kernel/syscalls/ioctl/ioctl_ns01.c index 06c81ba1..c2c95673 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_ns01.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_ns01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test ioctl_ns with NS_GET_PARENT request. * * Parent process tries to get parent of initial namespace, which should diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ns02.c b/testcases/kernel/syscalls/ioctl/ioctl_ns02.c index 3c958c14..c6849b53 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_ns02.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_ns02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test ioctl_ns with NS_GET_PARENT request. * * Test tries to get namespace parent for UTS namespace, which diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ns03.c b/testcases/kernel/syscalls/ioctl/ioctl_ns03.c index bdd80585..395423b0 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_ns03.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_ns03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test ioctl_ns with NS_GET_OWNER_UID request. * * Calls ioctl for a UTS namespace, which isn't a user namespace. diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ns04.c b/testcases/kernel/syscalls/ioctl/ioctl_ns04.c index 8c5c9f96..4385b6e3 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_ns04.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_ns04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test ioctl_ns with NS_GET_USERNS request. * * Owning user namespace of process calling ioctl is out of scope, diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ns05.c b/testcases/kernel/syscalls/ioctl/ioctl_ns05.c index 36e41c46..e84af38b 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_ns05.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_ns05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test ioctl_ns with NS_GET_PARENT request. * * Child cloned with the CLONE_NEWPID flag is created in a new pid namespace. diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ns06.c b/testcases/kernel/syscalls/ioctl/ioctl_ns06.c index 45fc01ce..a37ad627 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_ns06.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_ns06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test ioctl_ns with NS_GET_USERNS request. * * After the call to clone with the CLONE_NEWUSER flag, diff --git a/testcases/kernel/syscalls/ioctl/ioctl_ns07.c b/testcases/kernel/syscalls/ioctl/ioctl_ns07.c index 9cfa57fb..b0a5d0b1 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_ns07.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_ns07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test ioctl_ns with NS_GET_* request for file descriptors * that aren't namespaces. * diff --git a/testcases/kernel/syscalls/ioctl/ioctl_pidfd.h b/testcases/kernel/syscalls/ioctl/ioctl_pidfd.h new file mode 100644 index 00000000..2740284f --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_pidfd.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +#ifndef IOCTL_PIDFD_H +#define IOCTL_PIDFD_H + +#include "tst_test.h" +#include "lapi/pidfd.h" + +static inline int ioctl_pidfd_get_info_supported(void) +{ + pid_t pid; + int pidfd, ret; + int supported = 0; + struct pidfd_info info; + + if (tst_kvercmp(6, 13, 0) >= 0) + return 1; + + memset(&info, 0, sizeof(struct pidfd_info)); + + pid = SAFE_FORK(); + if (!pid) + exit(100); + + pidfd = SAFE_PIDFD_OPEN(pid, 0); + + ret = ioctl(pidfd, PIDFD_GET_INFO, &info); + SAFE_WAITPID(pid, NULL, 0); + + if (ret != -1) + supported = 1; + + SAFE_CLOSE(pidfd); + return supported; +} + +static inline int ioctl_pidfd_info_exit_supported(void) +{ + int ret; + pid_t pid; + int pidfd; + int supported = 0; + struct pidfd_info info; + + if (tst_kvercmp(6, 15, 0) >= 0) + return 1; + + memset(&info, 0, sizeof(struct pidfd_info)); + info.mask = PIDFD_INFO_EXIT; + + pid = SAFE_FORK(); + if (!pid) + exit(100); + + pidfd = SAFE_PIDFD_OPEN(pid, 0); + SAFE_WAITPID(pid, NULL, 0); + + ret = ioctl(pidfd, PIDFD_GET_INFO, &info); + if (ret == -1) { + /* - ENOTTY: old kernels not implementing fs/pidfs.c:pidfd_ioctl + * - EINVAL: until v6.13 kernel + * - ESRCH: all kernels between v6.13 and v6.15 + */ + if (errno != ENOTTY && + errno != EINVAL && + errno != ESRCH) + tst_brk(TBROK | TERRNO, "ioctl error"); + } else { + if (info.mask & PIDFD_INFO_EXIT) + supported = 1; + } + + SAFE_CLOSE(pidfd); + + return supported; +} + +#endif diff --git a/testcases/kernel/syscalls/ioctl/ioctl_pidfd01.c b/testcases/kernel/syscalls/ioctl/ioctl_pidfd01.c new file mode 100644 index 00000000..a786b25b --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_pidfd01.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Verify that ioctl() raises the right errors when an application provides + * the wrong file descriptor. + */ + +#include "ioctl_pidfd.h" + +static int exp_errnos_num; +static int exp_errnos[] = { + EINVAL, + EBADF, + ENOTTY, + EACCES, +}; + +static struct pidfd_info *info; + +static void test_bad_pidfd(struct tst_fd *fd_in) +{ + if (fd_in->type == TST_FD_PIDFD) { + tst_res(TINFO, "Skipping pidfd: SUCCESS"); + return; + } + + TST_EXP_FAIL_ARR(ioctl(fd_in->fd, PIDFD_GET_INFO, info), + exp_errnos, exp_errnos_num, + "ioctl(%s, PIDFD_GET_INFO, info)", + tst_fd_desc(fd_in)); +} + +static void run(void) +{ + TST_FD_FOREACH(fd) { + tst_res(TINFO, "%s -> ...", tst_fd_desc(&fd)); + test_bad_pidfd(&fd); + } +} + +static void setup(void) +{ + if (!ioctl_pidfd_info_exit_supported()) + tst_brk(TCONF, "PIDFD_INFO_EXIT is not supported by ioctl()"); + + exp_errnos_num = ARRAY_SIZE(exp_errnos) - 1; + + if (tst_selinux_enforcing()) + exp_errnos_num++; + + info->mask = PIDFD_INFO_EXIT; +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&info, .size = sizeof(*info)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_pidfd02.c b/testcases/kernel/syscalls/ioctl/ioctl_pidfd02.c new file mode 100644 index 00000000..858ec461 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_pidfd02.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Check if the ioctl() function allows retrieval of a child's exit code + * using PIDFD_INFO_EXIT from a process that can be isolated or not from the + * child. + */ + +#include "ioctl_pidfd.h" +#include "lapi/sched.h" + +static struct tst_clone_args *args; +static struct pidfd_info *info0, *info1; + +static void run(unsigned int isolate) +{ + int status; + int pidfd = 0; + pid_t pid_child; + + memset(args, 0, sizeof(struct tst_clone_args)); + memset(info0, 0, sizeof(struct pidfd_info)); + memset(info1, 0, sizeof(struct pidfd_info)); + + if (isolate) { + args->flags = CLONE_PIDFD | CLONE_NEWUSER | CLONE_NEWPID; + args->pidfd = (uint64_t)&pidfd; + args->exit_signal = SIGCHLD; + + pid_child = SAFE_CLONE(args); + } else { + pid_child = SAFE_FORK(); + } + + if (!pid_child) { + TST_CHECKPOINT_WAIT(0); + exit(100); + } + + if (!isolate) + pidfd = SAFE_PIDFD_OPEN(pid_child, 0); + + /* child is not reaped and ioctl() won't provide any exit status info */ + info0->mask = PIDFD_INFO_EXIT; + SAFE_IOCTL(pidfd, PIDFD_GET_INFO, info0); + TST_EXP_EQ_LI(info0->mask & PIDFD_INFO_EXIT, 0); + TST_EXP_EQ_LI(info0->exit_code, 0); + + TST_CHECKPOINT_WAKE(0); + + SAFE_WAITPID(pid_child, &status, 0); + + /* child is now reaped and ioctl() will provide the exit status */ + info1->mask = PIDFD_INFO_EXIT; + SAFE_IOCTL(pidfd, PIDFD_GET_INFO, info1); + SAFE_CLOSE(pidfd); + + TST_EXP_EQ_LI(info1->mask & PIDFD_INFO_EXIT, PIDFD_INFO_EXIT); + TST_EXP_EQ_LI(info1->exit_code, status); + + TST_EXP_EQ_LI(WEXITSTATUS(info1->exit_code), 100); +} + +static void setup(void) +{ + if (!ioctl_pidfd_info_exit_supported()) + tst_brk(TCONF, "PIDFD_INFO_EXIT is not supported by ioctl()"); +} + +static struct tst_test test = { + .test = run, + .setup = setup, + .tcnt = 2, + .forks_child = 1, + .needs_checkpoints = 1, + .bufs = (struct tst_buffers []) { + {&args, .size = sizeof(*args)}, + {&info0, .size = sizeof(*info0)}, + {&info1, .size = sizeof(*info1)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_pidfd03.c b/testcases/kernel/syscalls/ioctl/ioctl_pidfd03.c new file mode 100644 index 00000000..eca4b90f --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_pidfd03.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Verify that ioctl() returns ESRCH when a process attempts to access the + * exit status of an isolated child using `PIDFD_GET_INFO` and + * `PIDFD_INFO_EXIT` is not defined in `struct pidfd_info`. + */ + +#include "ioctl_pidfd.h" +#include "lapi/sched.h" + +static struct tst_clone_args *args; +static struct pidfd_info *info; + +static void run(void) +{ + int status; + int pidfd = 0; + pid_t pid_child; + + memset(args, 0, sizeof(struct tst_clone_args)); + + args->flags = CLONE_PIDFD | CLONE_NEWUSER | CLONE_NEWPID; + args->pidfd = (uint64_t)&pidfd; + args->exit_signal = SIGCHLD; + + pid_child = SAFE_CLONE(args); + if (!pid_child) + exit(100); + + info->mask = 0; + + /* child is not reaped, so ioctl() will pass */ + SAFE_IOCTL(pidfd, PIDFD_GET_INFO, info); + TST_EXP_EQ_LI(info->mask & PIDFD_INFO_EXIT, 0); + + SAFE_WAITPID(pid_child, &status, 0); + + /* child is now reaped, so we get ESRCH */ + TST_EXP_FAIL(ioctl(pidfd, PIDFD_GET_INFO, info), ESRCH); + TST_EXP_EQ_LI(info->mask & PIDFD_INFO_EXIT, 0); + + SAFE_CLOSE(pidfd); +} + +static void setup(void) +{ + if (!ioctl_pidfd_info_exit_supported()) + tst_brk(TCONF, "PIDFD_INFO_EXIT is not supported by ioctl()"); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&args, .size = sizeof(*args)}, + {&info, .size = sizeof(*info)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_pidfd04.c b/testcases/kernel/syscalls/ioctl/ioctl_pidfd04.c new file mode 100644 index 00000000..e5f58241 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_pidfd04.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Verify that ioctl() permits to obtain the exit code of an isolated signaled + * child via PIDFD_INFO_EXIT from within a process. + */ + +#include "ioctl_pidfd.h" +#include "lapi/sched.h" + +static struct tst_clone_args *args; +static struct pidfd_info *info; + +static void run(void) +{ + int status; + int pidfd = 0; + pid_t pid_child; + + memset(args, 0, sizeof(struct tst_clone_args)); + memset(info, 0, sizeof(struct pidfd_info)); + + info->mask = PIDFD_INFO_EXIT; + + args->flags = CLONE_PIDFD | CLONE_NEWUSER | CLONE_NEWPID; + args->pidfd = (uint64_t)&pidfd; + args->exit_signal = SIGCHLD; + + pid_child = SAFE_CLONE(args); + if (!pid_child) { + TST_CHECKPOINT_WAKE_AND_WAIT(0); + exit(1); + } + + TST_CHECKPOINT_WAIT(0); + + SAFE_KILL(pid_child, SIGKILL); + SAFE_WAITPID(pid_child, &status, 0); + + SAFE_IOCTL(pidfd, PIDFD_GET_INFO, info); + SAFE_CLOSE(pidfd); + + TST_EXP_EQ_LI(info->mask & PIDFD_INFO_EXIT, PIDFD_INFO_EXIT); + TST_EXP_EQ_LI(WIFSIGNALED(info->exit_code), WIFSIGNALED(status)); + TST_EXP_EQ_LI(WEXITSTATUS(info->exit_code), WEXITSTATUS(status)); + TST_EXP_EQ_LI(WTERMSIG(info->exit_code), WTERMSIG(status)); + + TST_EXP_EXPR(WIFSIGNALED(info->exit_code) && + WTERMSIG(info->exit_code) == SIGKILL); +} + +static void setup(void) +{ + if (!ioctl_pidfd_info_exit_supported()) + tst_brk(TCONF, "PIDFD_INFO_EXIT is not supported by ioctl()"); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .needs_checkpoints = 1, + .bufs = (struct tst_buffers []) { + {&args, .size = sizeof(*args)}, + {&info, .size = sizeof(*info)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_pidfd05.c b/testcases/kernel/syscalls/ioctl/ioctl_pidfd05.c new file mode 100644 index 00000000..d20c6f07 --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_pidfd05.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Verify that ioctl() raises an EINVAL error when PIDFD_GET_INFO is used. This + * happens when: + * + * - info parameter is NULL + * - info parameter is providing the wrong size + */ + +#include "tst_test.h" +#include "lapi/pidfd.h" +#include "lapi/sched.h" +#include "ioctl_pidfd.h" + +struct pidfd_info_invalid { + uint32_t dummy; +}; + +#define PIDFD_GET_INFO_SHORT _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info_invalid) + +static struct tst_clone_args *args; +static struct pidfd_info_invalid *info_invalid; + +static void run(void) +{ + int pidfd = 0; + pid_t pid_child; + + memset(args, 0, sizeof(struct tst_clone_args)); + + info_invalid->dummy = 1; + + args->flags = CLONE_PIDFD | CLONE_NEWUSER | CLONE_NEWPID; + args->pidfd = (uint64_t)&pidfd; + args->exit_signal = SIGCHLD; + + pid_child = SAFE_CLONE(args); + if (!pid_child) + exit(0); + + TST_EXP_FAIL(ioctl(pidfd, PIDFD_GET_INFO, NULL), EINVAL); + TST_EXP_FAIL(ioctl(pidfd, PIDFD_GET_INFO_SHORT, info_invalid), EINVAL); + + SAFE_CLOSE(pidfd); +} + +static void setup(void) +{ + if (!ioctl_pidfd_get_info_supported()) + tst_brk(TCONF, "ioctl(PIDFD_GET_INFO) is not implemented"); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&args, .size = sizeof(*args)}, + {&info_invalid, .size = sizeof(*info_invalid)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_pidfd06.c b/testcases/kernel/syscalls/ioctl/ioctl_pidfd06.c new file mode 100644 index 00000000..998f642e --- /dev/null +++ b/testcases/kernel/syscalls/ioctl/ioctl_pidfd06.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Verify that ioctl() doesn't allow to obtain the exit status of an isolated + * process via PIDFD_INFO_EXIT in within an another isolated process, which + * doesn't have any parent connection. + */ + +#include "ioctl_pidfd.h" +#include "lapi/sched.h" + +static struct tst_clone_args *args; +static struct pidfd_info *info; + +static void run(void) +{ + int pidfd; + pid_t pid_child; + + memset(args, 0, sizeof(struct tst_clone_args)); + memset(info, 0, sizeof(struct pidfd_info)); + + info->mask = PIDFD_INFO_EXIT; + + args->flags = CLONE_PIDFD | CLONE_NEWUSER | CLONE_NEWPID; + args->pidfd = (uint64_t)&pidfd; + args->exit_signal = SIGCHLD; + + pid_child = SAFE_CLONE(args); + if (!pid_child) + exit(100); + + SAFE_WAITPID(pid_child, NULL, 0); + + memset(args, 0, sizeof(struct tst_clone_args)); + + args->flags = CLONE_NEWUSER | CLONE_NEWPID; + args->exit_signal = SIGCHLD; + + if (!SAFE_CLONE(args)) { + TST_EXP_FAIL(ioctl(pidfd, PIDFD_GET_INFO, info), ESRCH); + exit(0); + } + + SAFE_CLOSE(pidfd); +} + +static void setup(void) +{ + if (!ioctl_pidfd_info_exit_supported()) + tst_brk(TCONF, "PIDFD_INFO_EXIT is not supported by ioctl()"); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&args, .size = sizeof(*args)}, + {&info, .size = sizeof(*info)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ioctl/ioctl_sg01.c b/testcases/kernel/syscalls/ioctl/ioctl_sg01.c index dfbba399..fba3816c 100755 --- a/testcases/kernel/syscalls/ioctl/ioctl_sg01.c +++ b/testcases/kernel/syscalls/ioctl/ioctl_sg01.c @@ -3,7 +3,7 @@ * Copyright (c) 2019 SUSE LLC */ -/* +/*\ * CVE-2018-1000204 * * Test ioctl(SG_IO) and check that kernel doesn't leak data. Requires @@ -126,7 +126,7 @@ static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 3600, + .timeout = 3600, .tags = (const struct tst_tag[]) { {"linux-git", "a45b599ad808"}, {"CVE", "2018-1000204"}, diff --git a/testcases/kernel/syscalls/ioctl/test_ioctl b/testcases/kernel/syscalls/ioctl/test_ioctl index 43836a22..8549e560 100755 --- a/testcases/kernel/syscalls/ioctl/test_ioctl +++ b/testcases/kernel/syscalls/ioctl/test_ioctl @@ -45,7 +45,7 @@ case "$device_no" in continue fi tst_resm TINFO "Testing ioctl02 with $tttype" - ioctl02 -D $tttype + ioctl02 -d $tttype RC=$? if [ $RC -eq 0 ] then diff --git a/testcases/kernel/syscalls/iopl/iopl01.c b/testcases/kernel/syscalls/iopl/iopl01.c index 70e2a7ff..227344aa 100755 --- a/testcases/kernel/syscalls/iopl/iopl01.c +++ b/testcases/kernel/syscalls/iopl/iopl01.c @@ -1,17 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) Linux Test Project, 2020 - * Copyright (c) Wipro Technologies Ltd, 2002 + * Copyright (c) Linux Test Project, 2003-2024 + * Copyright (c) Wipro Technologies Ltd, 2002 + * Author: Subhab Biswas */ -/* +/*\ * This is a basic test for iopl(2) system call. * - * Test the system call for possible privelege levels. - * As the privelge level for a normal process is 0, start by + * Test the system call for possible privilege levels. + * As the privilege level for a normal process is 0, start by * setting/changing the level to 0. - * - * Author: Subhab Biswas */ #include diff --git a/testcases/kernel/syscalls/iopl/iopl02.c b/testcases/kernel/syscalls/iopl/iopl02.c index 7301442f..bf3e0206 100755 --- a/testcases/kernel/syscalls/iopl/iopl02.c +++ b/testcases/kernel/syscalls/iopl/iopl02.c @@ -1,19 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) Linux Test Project, 2020 - * Copyright (c) Wipro Technologies Ltd, 2002 + * Copyright (c) Linux Test Project, 2020 + * Copyright (c) Wipro Technologies Ltd, 2002 + * Author: Subhab Biswas */ -/* - * This is an error test for iopl(2) system call. +/*\ + * Test for iopl(2) system call error. * - * Verify that - * 1) iopl(2) returns -1 and sets errno to EINVAL for privilege - * level greater than 3. - * 2) iopl(2) returns -1 and sets errno to EPERM if the current - * user is not the super-user. - * - * Author: Subhab Biswas + * - iopl(2) fail with EINVAL when privilege level greater than 3. + * - iopl(2) fail with EPERM when the current user is not the super-user. */ #include diff --git a/testcases/kernel/syscalls/ioprio/ioprio_get01.c b/testcases/kernel/syscalls/ioprio/ioprio_get01.c index f1325be3..a0c919e2 100755 --- a/testcases/kernel/syscalls/ioprio/ioprio_get01.c +++ b/testcases/kernel/syscalls/ioprio/ioprio_get01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic ioprio_get() test. Gets the current process I/O priority and * checks that the values are sane. */ diff --git a/testcases/kernel/syscalls/ioprio/ioprio_set01.c b/testcases/kernel/syscalls/ioprio/ioprio_set01.c index 0868cea7..a69b28ec 100755 --- a/testcases/kernel/syscalls/ioprio/ioprio_set01.c +++ b/testcases/kernel/syscalls/ioprio/ioprio_set01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic ioprio_set() test. Gets the current process I/O priority and * bumps it up one notch, then down two notches and checks that the * new priority is reported back correctly. diff --git a/testcases/kernel/syscalls/ioprio/ioprio_set02.c b/testcases/kernel/syscalls/ioprio/ioprio_set02.c index 37db7bf4..39ef38a8 100755 --- a/testcases/kernel/syscalls/ioprio/ioprio_set02.c +++ b/testcases/kernel/syscalls/ioprio/ioprio_set02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Extended ioprio_set() test. * Tests to set all 8 priority levels for best effort priority, then * switches to test all 8 priority levels for idle priority. diff --git a/testcases/kernel/syscalls/ioprio/ioprio_set03.c b/testcases/kernel/syscalls/ioprio/ioprio_set03.c index 6efcacc1..6a072c6c 100755 --- a/testcases/kernel/syscalls/ioprio/ioprio_set03.c +++ b/testcases/kernel/syscalls/ioprio/ioprio_set03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Negative ioprio_set() test. Test some non-working priorities to make * sure they don't work. */ diff --git a/testcases/kernel/syscalls/ipc/msgctl/Makefile b/testcases/kernel/syscalls/ipc/msgctl/Makefile index 6b2b26d0..baa2a48e 100755 --- a/testcases/kernel/syscalls/ipc/msgctl/Makefile +++ b/testcases/kernel/syscalls/ipc/msgctl/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/msgctl/msgctl05.c b/testcases/kernel/syscalls/ipc/msgctl/msgctl05.c index cd2643bc..7e059d7f 100755 --- a/testcases/kernel/syscalls/ipc/msgctl/msgctl05.c +++ b/testcases/kernel/syscalls/ipc/msgctl/msgctl05.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Viresh Kumar - * - * Description: + */ + +/*\ * Cross verify the _high fields being set to 0 by the kernel. */ #include diff --git a/testcases/kernel/syscalls/ipc/msgctl/msgctl06.c b/testcases/kernel/syscalls/ipc/msgctl/msgctl06.c index 6f547638..2e22f37f 100755 --- a/testcases/kernel/syscalls/ipc/msgctl/msgctl06.c +++ b/testcases/kernel/syscalls/ipc/msgctl/msgctl06.c @@ -4,8 +4,6 @@ * Author: Feiyu Zhu */ /*\ - * [Description] - * * Call msgctl() with MSG_INFO flag and check that: * * * The returned index points to a valid MSG by calling MSG_STAT_ANY @@ -139,12 +137,16 @@ static void verify_msgctl(unsigned int n) static void setup(void) { struct msqid_ds temp_buf; + struct buf { + long type; + char text[5]; + } msgbuf = {MSGTYPE, "abcd"}; ltpuser = SAFE_GETPWNAM("nobody"); nobody_uid = ltpuser->pw_uid; root_uid = 0; msg_id = SAFE_MSGGET(IPC_PRIVATE, IPC_CREAT | MSG_RW); - SAFE_MSGSND(msg_id, "abcd", 4, 0); + SAFE_MSGSND(msg_id, &msgbuf, sizeof(msgbuf.text), 0); TEST(msgctl(msg_id, MSG_STAT_ANY, &temp_buf)); if (TST_RET == -1) { diff --git a/testcases/kernel/syscalls/ipc/msgget/Makefile b/testcases/kernel/syscalls/ipc/msgget/Makefile index 6b2b26d0..baa2a48e 100755 --- a/testcases/kernel/syscalls/ipc/msgget/Makefile +++ b/testcases/kernel/syscalls/ipc/msgget/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/msgget/msgget01.c b/testcases/kernel/syscalls/ipc/msgget/msgget01.c index 2ab34ff9..dd5a5ad7 100755 --- a/testcases/kernel/syscalls/ipc/msgget/msgget01.c +++ b/testcases/kernel/syscalls/ipc/msgget/msgget01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Create a message queue, write a message to it and * read it back. */ diff --git a/testcases/kernel/syscalls/ipc/msgget/msgget02.c b/testcases/kernel/syscalls/ipc/msgget/msgget02.c index 1885599d..ef9ab34b 100755 --- a/testcases/kernel/syscalls/ipc/msgget/msgget02.c +++ b/testcases/kernel/syscalls/ipc/msgget/msgget02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test for EEXIST, ENOENT, EACCES errors. * * - msgget(2) fails if a message queue exists for key and msgflg diff --git a/testcases/kernel/syscalls/ipc/msgget/msgget03.c b/testcases/kernel/syscalls/ipc/msgget/msgget03.c index 9e6c66cb..bd065397 100755 --- a/testcases/kernel/syscalls/ipc/msgget/msgget03.c +++ b/testcases/kernel/syscalls/ipc/msgget/msgget03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test for ENOSPC error. * * ENOSPC - All possible message queues have been taken (MSGMNI) diff --git a/testcases/kernel/syscalls/ipc/msgget/msgget04.c b/testcases/kernel/syscalls/ipc/msgget/msgget04.c index a01ecea8..28cbb335 100755 --- a/testcases/kernel/syscalls/ipc/msgget/msgget04.c +++ b/testcases/kernel/syscalls/ipc/msgget/msgget04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for msg_next_id. * msg_next_id specifies desired id for next allocated IPC message. By * default it's equal to -1, which means generic allocation logic. diff --git a/testcases/kernel/syscalls/ipc/msgget/msgget05.c b/testcases/kernel/syscalls/ipc/msgget/msgget05.c index 817c8486..edcb307c 100755 --- a/testcases/kernel/syscalls/ipc/msgget/msgget05.c +++ b/testcases/kernel/syscalls/ipc/msgget/msgget05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for msg_next_id. * When the message queue identifier that msg_next_id stored is already in use, * call msgget with different key just use another unused value in range diff --git a/testcases/kernel/syscalls/ipc/msgrcv/Makefile b/testcases/kernel/syscalls/ipc/msgrcv/Makefile index 6b2b26d0..baa2a48e 100755 --- a/testcases/kernel/syscalls/ipc/msgrcv/Makefile +++ b/testcases/kernel/syscalls/ipc/msgrcv/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/msgrcv/msgrcv01.c b/testcases/kernel/syscalls/ipc/msgrcv/msgrcv01.c index 9df20a61..58bfd277 100755 --- a/testcases/kernel/syscalls/ipc/msgrcv/msgrcv01.c +++ b/testcases/kernel/syscalls/ipc/msgrcv/msgrcv01.c @@ -26,13 +26,13 @@ static void verify_msgrcv(void) SAFE_MSGSND(queue_id, &snd_buf, MSGSIZE, 0); - before_rcv = tst_get_fs_timestamp(); + before_rcv = tst_fs_timestamp_start(); TEST(msgrcv(queue_id, &rcv_buf, MSGSIZE, 1, 0)); if (TST_RET == -1) { tst_res(TFAIL | TTERRNO, "msgrcv failed"); return; } - after_rcv = tst_get_fs_timestamp(); + after_rcv = tst_fs_timestamp_end(); if (strcmp(rcv_buf.mtext, snd_buf.mtext) == 0) tst_res(TPASS, "message received(%s) = message sent(%s)", diff --git a/testcases/kernel/syscalls/ipc/msgsnd/Makefile b/testcases/kernel/syscalls/ipc/msgsnd/Makefile index 85017fe9..0b7b7bd4 100755 --- a/testcases/kernel/syscalls/ipc/msgsnd/Makefile +++ b/testcases/kernel/syscalls/ipc/msgsnd/Makefile @@ -3,11 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc - -ifeq ($(UCLINUX),1) -FILTER_OUT_MAKE_TARGETS += msgsnd05 msgsnd06 -endif +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/msgsnd/msgsnd01.c b/testcases/kernel/syscalls/ipc/msgsnd/msgsnd01.c index 8232f0ca..6d92f3de 100755 --- a/testcases/kernel/syscalls/ipc/msgsnd/msgsnd01.c +++ b/testcases/kernel/syscalls/ipc/msgsnd/msgsnd01.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) Linux Test Project, 2002-2024 */ -/* - * DESCRIPTION - * test that msgsnd() enqueues a message correctly. +/*\ + * Verify that msgsnd(2) enqueues a message correctly. */ #include @@ -30,13 +30,13 @@ static void verify_msgsnd(void) struct msqid_ds qs_buf; time_t before_snd, after_snd; - before_snd = tst_get_fs_timestamp(); + before_snd = tst_fs_timestamp_start(); TEST(msgsnd(queue_id, &snd_buf, MSGSIZE, 0)); if (TST_RET == -1) { tst_res(TFAIL | TTERRNO, "msgsnd() failed"); return; } - after_snd = tst_get_fs_timestamp(); + after_snd = tst_fs_timestamp_end(); SAFE_MSGCTL(queue_id, IPC_STAT, &qs_buf); diff --git a/testcases/kernel/syscalls/ipc/msgsnd/msgsnd05.c b/testcases/kernel/syscalls/ipc/msgsnd/msgsnd05.c index f048fa69..887bfdef 100755 --- a/testcases/kernel/syscalls/ipc/msgsnd/msgsnd05.c +++ b/testcases/kernel/syscalls/ipc/msgsnd/msgsnd05.c @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) Linux Test Project, 2002-2024 */ -/* - * DESCRIPTION - * 1) msgsnd(2) fails and sets errno to EAGAIN if the message can't be - * sent due to the msg_qbytes limit for the queue and IPC_NOWAIT is - * specified. - * 2) msgsnd(2) fails and sets errno to EINTR if msgsnd(2) sleeps on a - * full message queue condition and the process catches a signal. +/*\ + * Verify that msgsnd(2) fails and sets correct errno: + * + * - EAGAIN if the message can't be sent due to the msg_qbytes limit for the + * queue and IPC_NOWAIT is specified + * - EINTR if msgsnd(2) sleeps on a full message queue condition and the process + * catches a signal */ #include diff --git a/testcases/kernel/syscalls/ipc/msgstress/.gitignore b/testcases/kernel/syscalls/ipc/msgstress/.gitignore index a8f67539..fe978069 100755 --- a/testcases/kernel/syscalls/ipc/msgstress/.gitignore +++ b/testcases/kernel/syscalls/ipc/msgstress/.gitignore @@ -1,4 +1 @@ /msgstress01 -/msgstress02 -/msgstress03 -/msgstress04 diff --git a/testcases/kernel/syscalls/ipc/msgstress/Makefile b/testcases/kernel/syscalls/ipc/msgstress/Makefile index b821bda0..442eb87d 100755 --- a/testcases/kernel/syscalls/ipc/msgstress/Makefile +++ b/testcases/kernel/syscalls/ipc/msgstress/Makefile @@ -3,10 +3,5 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpipc - include $(top_srcdir)/include/mk/testcases.mk - -LTPLDLIBS += -lltpipc - include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/ipc/msgstress/msgstress01.c b/testcases/kernel/syscalls/ipc/msgstress/msgstress01.c index 84e33843..10c9adcb 100755 --- a/testcases/kernel/syscalls/ipc/msgstress/msgstress01.c +++ b/testcases/kernel/syscalls/ipc/msgstress/msgstress01.c @@ -1,301 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 - * - * 06/30/2001 Port to Linux nsharoff@us.ibm.com - * 11/06/2002 Port to LTP dbarrera@us.ibm.com + * Copyright (c) International Business Machines Corp., 2002 + * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * 11/11/2002 Port to LTP dbarrera@us.ibm.com + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -/* - * Get and manipulate a message queue. +/*\ + * Stress test for SysV IPC. We send multiple messages at the same time, + * checking that we are not loosing any byte once we receive the messages + * from multiple children. + * + * The number of messages to send is determined by the free slots available + * in SysV IPC and the available number of children which can be spawned by + * the process. Each sender will spawn multiple messages at the same time and + * each receiver will read them one by one. */ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "ipcmsg.h" -#include "libmsgctl.h" +#include "tst_safe_sysv_ipc.h" +#include "tst_safe_stdio.h" +#include "tst_test.h" -char *TCID = "msgstress01"; -int TST_TOTAL = 1; +#define SYSVIPC_TOTAL "/proc/sys/kernel/msgmni" +#define SYSVIPC_USED "/proc/sysvipc/msg" +#define MSGTYPE 10 +#define MAXNREPS 100000 -#ifndef CONFIG_COLDFIRE -#define MAXNPROCS 1000000 /* This value is set to an arbitrary high limit. */ -#else -#define MAXNPROCS 100000 /* Coldfire can't deal with 1000000 */ -#endif -#define MAXNREPS 100000 - -static key_t keyarray[MAXNPROCS]; -static int pidarray[MAXNPROCS]; -static int tid; -static int MSGMNI, nprocs, nreps; -static int procstat; -static int mykid; - -void setup(void); -void cleanup(void); - -static int dotest(key_t key, int child_process); -static void sig_handler(); - -static char *opt_nprocs; -static char *opt_nreps; - -static option_t options[] = { - {"n:", NULL, &opt_nprocs}, - {"l:", NULL, &opt_nreps}, - {NULL, NULL, NULL}, +struct sysv_msg { + long type; + struct { + char len; + char pbytes[99]; + } data; }; -static void usage(void) +struct sysv_data { + int id; + struct sysv_msg msg; +}; + +static struct sysv_data *ipc_data; +static int ipc_data_len; + +static char *str_num_messages; +static char *str_num_iterations; +static int num_messages = 1000; +static int num_iterations = MAXNREPS; +static volatile int *stop; +static volatile int *fail; +static tst_atomic_t *finished; +static int *flags; + +static int get_used_sysvipc(void) { - printf(" -n Number of processes\n"); - printf(" -l Number of iterations\n"); + FILE *fp; + int used = -1; + char buf[BUFSIZ]; + + fp = SAFE_FOPEN(SYSVIPC_USED, "r"); + + while (fgets(buf, BUFSIZ, fp) != NULL) + used++; + + SAFE_FCLOSE(fp); + + if (used < 0) + tst_brk(TBROK, "Can't read %s to get used sysvipc resource", SYSVIPC_USED); + + return used; } -int main(int argc, char **argv) +static void reset_messages(void) { - int i, j, ok, pid; - int count, status; - struct sigaction act; + ipc_data_len = 0; + memset(ipc_data, 0, sizeof(struct sysv_data) * num_messages); - tst_parse_opts(argc, argv, options, usage); + for (int i = 0; i < num_messages; i++) + ipc_data[i].id = -1; - setup(); + *stop = 0; + *fail = 0; + *finished = 0; +} - nreps = MAXNREPS; - nprocs = MSGMNI; +static int create_message(const int id) +{ + int pos = ipc_data_len; + struct sysv_data *buff = ipc_data + pos; - if (opt_nreps) { - nreps = atoi(opt_nreps); - if (nreps > MAXNREPS) { - tst_resm(TINFO, - "Requested number of iterations too large, " - "setting to Max. of %d", MAXNREPS); - nreps = MAXNREPS; - } - } + buff->id = id; + buff->msg.type = MSGTYPE; + buff->msg.data.len = (rand() % 99) + 1; - if (opt_nprocs) { - nprocs = atoi(opt_nprocs); - if (nprocs > MSGMNI) { - tst_resm(TINFO, - "Requested number of processes too large, " - "setting to Max. of %d", MSGMNI); - nprocs = MSGMNI; - } - } + for (int i = 0; i < buff->msg.data.len; i++) + buff->msg.data.pbytes[i] = rand() % 255; - srand(getpid()); - tid = -1; + ipc_data_len++; - /* Setup signal handling routine */ - memset(&act, 0, sizeof(act)); - act.sa_handler = sig_handler; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask, SIGTERM); - if (sigaction(SIGTERM, &act, NULL) < 0) { - tst_brkm(TFAIL, NULL, "Sigset SIGTERM failed"); - } - /* Set up array of unique keys for use in allocating message - * queues - */ - for (i = 0; i < nprocs; i++) { - ok = 1; - do { - /* Get random key */ - keyarray[i] = (key_t) rand(); - /* Make sure key is unique and not private */ - if (keyarray[i] == IPC_PRIVATE) { - ok = 0; + return pos; +} + +static void writer(const int id, const int pos) +{ + struct sysv_data *buff = &ipc_data[pos]; + int iter = num_iterations; + + while (--iter >= 0 && !(*stop)) { + int size = msgsnd(id, &buff->msg, 100, IPC_NOWAIT); + + if (size < 0) { + if (errno == EAGAIN) { + usleep(10); continue; } - for (j = 0; j < i; j++) { - if (keyarray[j] == keyarray[i]) { - ok = 0; - break; - } - ok = 1; - } - } while (ok == 0); + + tst_brk(TBROK | TERRNO, "msgsnd() failed"); + } } - /* Fork a number of processes, each of which will - * create a message queue with one reader/writer - * pair which will read and write a number (iterations) - * of random length messages with specific values. + tst_atomic_inc(finished); +} + +static void reader(const int id, const int pos) +{ + int size; + int iter = num_iterations; + struct sysv_msg msg_recv; + struct sysv_data *buff = &ipc_data[pos]; + + while (--iter >= 0 && !(*stop)) { + memset(&msg_recv, 0, sizeof(struct sysv_msg)); + + size = msgrcv(id, &msg_recv, 100, MSGTYPE, IPC_NOWAIT); + if (size < 0) { + if (errno == ENOMSG) { + usleep(10); + continue; + } + + tst_brk(TBROK | TERRNO, "msgrcv() failed"); + } + + if (msg_recv.type != buff->msg.type) { + tst_res(TFAIL, "Received the wrong message type"); + + *stop = 1; + *fail = 1; + return; + } + + if (msg_recv.data.len != buff->msg.data.len) { + tst_res(TFAIL, "Received the wrong message data length"); + + *stop = 1; + *fail = 1; + return; + } + + for (int i = 0; i < msg_recv.data.len; i++) { + if (msg_recv.data.pbytes[i] != buff->msg.data.pbytes[i]) { + tst_res(TFAIL, "Received wrong data at index %d: %x != %x", i, + msg_recv.data.pbytes[i], + buff->msg.data.pbytes[i]); + + *stop = 1; + *fail = 1; + return; + } + } + + tst_res(TDEBUG, "Received correct data"); + tst_res(TDEBUG, "msg_recv.type = %ld", msg_recv.type); + tst_res(TDEBUG, "msg_recv.data.len = %d", msg_recv.data.len); + } + + tst_atomic_inc(finished); +} + +static void remove_queues(void) +{ + for (int pos = 0; pos < num_messages; pos++) { + struct sysv_data *buff = &ipc_data[pos]; + + if (buff->id != -1) + SAFE_MSGCTL(buff->id, IPC_RMID, NULL); + } +} + +static void run(void) +{ + int id, pos; + + reset_messages(); + + for (int i = 0; i < num_messages; i++) { + id = SAFE_MSGGET(IPC_PRIVATE, IPC_CREAT | 0600); + pos = create_message(id); + + if (!SAFE_FORK()) { + writer(id, pos); + return; + } + + if (!SAFE_FORK()) { + reader(id, pos); + return; + } + + if (*stop) + break; + + if (!tst_remaining_runtime()) { + tst_res(TCONF, "Out of runtime during forking..."); + *stop = 1; + break; + } + } + + if (!(*stop)) + tst_res(TINFO, "All processes running"); + + for (;;) { + if (tst_atomic_load(finished) == 2 * num_messages) + break; + + if (*stop) + break; + + if (!tst_remaining_runtime()) { + tst_res(TINFO, "Out of runtime, stopping processes..."); + *stop = 1; + break; + } + + sleep(1); + } + + tst_reap_children(); + remove_queues(); + + if (!(*fail)) + tst_res(TPASS, "Some messages received"); +} + +static void setup(void) +{ + int total_msg; + int avail_msg; + int free_msgs; + int free_pids; + + srand(time(0)); + + SAFE_FILE_SCANF(SYSVIPC_TOTAL, "%i", &total_msg); + + free_msgs = total_msg - get_used_sysvipc(); + + /* We remove 10% of free pids, just to be sure + * we won't saturate the system with processes. */ + free_pids = tst_get_free_pids() / 2.1; - for (i = 0; i < nprocs; i++) { - fflush(stdout); - if ((pid = FORK_OR_VFORK()) < 0) { - tst_brkm(TFAIL, - NULL, - "\tFork failed (may be OK if under stress)"); - } - /* Child does this */ - if (pid == 0) { - procstat = 1; - exit(dotest(keyarray[i], i)); - } - pidarray[i] = pid; + avail_msg = MIN(free_msgs, free_pids); + if (!avail_msg) + tst_brk(TCONF, "Unavailable messages slots"); + + tst_res(TINFO, "Available messages slots: %d", avail_msg); + + if (tst_parse_int(str_num_messages, &num_messages, 1, avail_msg)) + tst_brk(TBROK, "Invalid number of messages '%s'", str_num_messages); + + if (num_messages > avail_msg) { + tst_res(avail_msg < num_messages / 2 ? TWARN : TINFO, + "Number of messages reduced to %d", avail_msg); + num_messages = avail_msg; } - count = 0; - while (1) { - if ((wait(&status)) > 0) { - if (status >> 8 != 0) { - tst_brkm(TFAIL, NULL, - "Child exit status = %d", - status >> 8); - } - count++; - } else { - if (errno != EINTR) { - break; - } -#ifdef DEBUG - tst_resm(TINFO, "Signal detected during wait"); -#endif - } - } - /* Make sure proper number of children exited */ - if (count != nprocs) { - tst_brkm(TFAIL, - NULL, - "Wrong number of children exited, Saw %d, Expected %d", - count, nprocs); - } + if (tst_parse_int(str_num_iterations, &num_iterations, 1, MAXNREPS)) + tst_brk(TBROK, "Invalid number of messages iterations '%s'", str_num_iterations); - tst_resm(TPASS, "Test ran successfully!"); + ipc_data = SAFE_MMAP( + NULL, + sizeof(struct sysv_data) * num_messages, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, + -1, 0); - cleanup(); - tst_exit(); + flags = SAFE_MMAP( + NULL, + sizeof(int) * 3, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, + -1, 0); + + stop = &flags[0]; + fail = &flags[1]; + finished = &flags[2]; } -static int dotest(key_t key, int child_process) +static void cleanup(void) { - int id, pid; - int ret, status; + if (!ipc_data) + return; - sighold(SIGTERM); - TEST(msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)); - if (TEST_RETURN < 0) { - printf("msgget() error in child %d: %s\n", - child_process, strerror(TEST_ERRNO)); + remove_queues(); - return FAIL; - } - tid = id = TEST_RETURN; - sigrelse(SIGTERM); - - fflush(stdout); - if ((pid = FORK_OR_VFORK()) < 0) { - printf("\tFork failed (may be OK if under stress)\n"); - TEST(msgctl(tid, IPC_RMID, 0)); - if (TEST_RETURN < 0) { - printf("mscgtl() error in cleanup: %s\n", - strerror(TEST_ERRNO)); - } - return FAIL; - } - /* Child does this */ - if (pid == 0) - exit(doreader(key, id, 1, child_process, nreps)); - /* Parent does this */ - mykid = pid; - procstat = 2; - ret = dowriter(key, id, 1, child_process, nreps); - wait(&status); - - if (ret != PASS) - exit(FAIL); - - if ((!WIFEXITED(status) || (WEXITSTATUS(status) != PASS))) - exit(FAIL); - - TEST(msgctl(id, IPC_RMID, 0)); - if (TEST_RETURN < 0) { - printf("msgctl() errno %d: %s\n", - TEST_ERRNO, strerror(TEST_ERRNO)); - - return FAIL; - } - return PASS; + SAFE_MUNMAP(ipc_data, sizeof(struct sysv_data) * num_messages); + SAFE_MUNMAP(flags, sizeof(int) * 3); } -static void sig_handler(void) -{ -} - -void setup(void) -{ - int nr_msgqs; - - tst_tmpdir(); - - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - nr_msgqs = get_max_msgqueues(); - if (nr_msgqs < 0) - cleanup(); - - nr_msgqs -= get_used_msgqueues(); - if (nr_msgqs <= 0) { - tst_resm(TBROK, - "Max number of message queues already used, cannot create more."); - cleanup(); - } - - /* - * Since msgmni scales to the memory size, it may reach huge values - * that are not necessary for this test. - * That's why we define NR_MSGQUEUES as a high boundary for it. - */ - MSGMNI = MIN(nr_msgqs, NR_MSGQUEUES); -} - -void cleanup(void) -{ - int status; - -#ifdef DEBUG - tst_resm(TINFO, "Removing the message queue"); -#endif - (void)msgctl(tid, IPC_RMID, NULL); - if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) { - (void)msgctl(tid, IPC_RMID, NULL); - tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed"); - - } - - tst_rmdir(); -} +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .runtime = 180, + .options = (struct tst_option[]) { + {"n:", &str_num_messages, "Number of messages to send (default: 1000)"}, + {"l:", &str_num_iterations, "Number iterations per message (default: " + TST_TO_STR(MAXNREPS) ")"}, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/ipc/msgstress/msgstress02.c b/testcases/kernel/syscalls/ipc/msgstress/msgstress02.c deleted file mode 100755 index a0f894b0..00000000 --- a/testcases/kernel/syscalls/ipc/msgstress/msgstress02.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 - * - * 06/30/2001 Port to Linux nsharoff@us.ibm.com - * 11/11/2002 Port to LTP dbarrera@us.ibm.com - */ - -/* - * Get and manipulate a message queue. - */ - -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "ipcmsg.h" -#include "libmsgctl.h" - -char *TCID = "msgstress02"; -int TST_TOTAL = 1; - -#define MAXNREPS 1000 -#ifndef CONFIG_COLDFIRE -#define MAXNPROCS 1000000 /* This value is set to an arbitrary high limit. */ -#else -#define MAXNPROCS 100000 /* Coldfire can't deal with 1000000 */ -#endif -#define MAXNKIDS 10 - -static key_t keyarray[MAXNPROCS]; -static int pidarray[MAXNPROCS]; -static int rkidarray[MAXNKIDS]; -static int wkidarray[MAXNKIDS]; -static int tid; -static int nprocs, nreps, nkids, MSGMNI; -static int procstat; - -void setup(void); -void cleanup(void); - -static void term(int); -static int dotest(key_t, int); -static void cleanup_msgqueue(int i, int tid); - -static char *opt_nprocs; -static char *opt_nkids; -static char *opt_nreps; - -static option_t options[] = { - {"n:", NULL, &opt_nprocs}, - {"c:", NULL, &opt_nkids}, - {"l:", NULL, &opt_nreps}, - {NULL, NULL, NULL}, -}; - -static void usage(void) -{ - printf(" -n Number of processes\n"); - printf(" -c Number of read/write child pairs\n"); - printf(" -l Number of iterations\n"); -} - -int main(int argc, char **argv) -{ - int i, j, ok, pid; - int count, status; - - tst_parse_opts(argc, argv, options, usage); - - setup(); - - nreps = MAXNREPS; - nprocs = MSGMNI; - nkids = MAXNKIDS; - - if (opt_nreps) { - nreps = atoi(opt_nreps); - if (nreps > MAXNREPS) { - tst_resm(TINFO, - "Requested number of iterations too large, " - "setting to Max. of %d", MAXNREPS); - nreps = MAXNREPS; - } - } - - if (opt_nprocs) { - nprocs = atoi(opt_nprocs); - if (nprocs > MSGMNI) { - tst_resm(TINFO, - "Requested number of processes too large, " - "setting to Max. of %d", MSGMNI); - nprocs = MSGMNI; - } - } - - if (opt_nkids) { - nkids = atoi(opt_nkids); - if (nkids > MAXNKIDS) { - tst_resm(TINFO, - "Requested number of read/write pairs too " - "large, setting to Max. of %d", MAXNKIDS); - nkids = MAXNKIDS; - } - } - - procstat = 0; - srand48((unsigned)getpid() + (unsigned)(getppid() << 16)); - tid = -1; - - /* Setup signal handleing routine */ - if (sigset(SIGTERM, term) == SIG_ERR) { - tst_brkm(TFAIL, NULL, "Sigset SIGTERM failed"); - } - /* Set up array of unique keys for use in allocating message - * queues - */ - for (i = 0; i < nprocs; i++) { - ok = 1; - do { - /* Get random key */ - keyarray[i] = (key_t) lrand48(); - /* Make sure key is unique and not private */ - if (keyarray[i] == IPC_PRIVATE) { - ok = 0; - continue; - } - for (j = 0; j < i; j++) { - if (keyarray[j] == keyarray[i]) { - ok = 0; - break; - } - ok = 1; - } - } while (ok == 0); - } - /* Fork a number of processes (nprocs), each of which will - * create a message queue with several (nkids) reader/writer - * pairs which will read and write a number (iterations) - * of random length messages with specific values (keys). - */ - - for (i = 0; i < nprocs; i++) { - fflush(stdout); - if ((pid = FORK_OR_VFORK()) < 0) { - tst_brkm(TFAIL, - NULL, - "\tFork failed (may be OK if under stress)"); - } - /* Child does this */ - if (pid == 0) { - procstat = 1; - exit(dotest(keyarray[i], i)); - } - pidarray[i] = pid; - } - - count = 0; - while (1) { - if ((wait(&status)) > 0) { - if (status >> 8 != PASS) { - tst_brkm(TFAIL, NULL, - "Child exit status = %d", - status >> 8); - } - count++; - } else { - if (errno != EINTR) { - break; - } -#ifdef DEBUG - tst_resm(TINFO, "Signal detected during wait"); -#endif - } - } - /* Make sure proper number of children exited */ - if (count != nprocs) { - tst_brkm(TFAIL, - NULL, - "Wrong number of children exited, Saw %d, Expected %d", - count, nprocs); - } - - tst_resm(TPASS, "Test ran successfully!"); - - cleanup(); - tst_exit(); -} - -static void cleanup_msgqueue(int i, int tid) -{ - /* - * Decrease the value of i by 1 because it - * is getting incremented even if the fork - * is failing. - */ - - i--; - /* - * Kill all children & free message queue. - */ - for (; i >= 0; i--) { - (void)kill(rkidarray[i], SIGKILL); - (void)kill(wkidarray[i], SIGKILL); - } - - if (msgctl(tid, IPC_RMID, 0) < 0) { - tst_brkm(TFAIL | TERRNO, NULL, "Msgctl error in cleanup"); - } -} - -static int dotest(key_t key, int child_process) -{ - int id, pid; - int i, count, status, exit_status; - - sighold(SIGTERM); - if ((id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0) { - printf("msgget() error in child %d: %s\n", - child_process, strerror(errno)); - return FAIL; - } - tid = id; - sigrelse(SIGTERM); - - exit_status = PASS; - - for (i = 0; i < nkids; i++) { - fflush(stdout); - if ((pid = FORK_OR_VFORK()) < 0) { - printf("Fork failure in the first child of child group %d\n", - child_process); - cleanup_msgqueue(i, tid); - return FAIL; - } - /* First child does this */ - if (pid == 0) { - procstat = 2; - exit(doreader(key, tid, getpid(), - child_process, nreps)); - } - rkidarray[i] = pid; - fflush(stdout); - if ((pid = FORK_OR_VFORK()) < 0) { - printf("Fork failure in the second child of child group %d\n", - child_process); - /* - * Kill the reader child process - */ - (void)kill(rkidarray[i], SIGKILL); - - cleanup_msgqueue(i, tid); - return FAIL; - } - /* Second child does this */ - if (pid == 0) { - procstat = 2; - exit(dowriter(key, tid, rkidarray[i], - child_process, nreps)); - } - wkidarray[i] = pid; - } - /* Parent does this */ - count = 0; - while (1) { - if ((wait(&status)) > 0) { - if (status >> 8 != PASS) { - printf("Child exit status = %d from child group %d\n", - status >> 8, child_process); - for (i = 0; i < nkids; i++) { - kill(rkidarray[i], SIGTERM); - kill(wkidarray[i], SIGTERM); - } - if (msgctl(tid, IPC_RMID, 0) < 0) { - printf("msgctl() error: %s\n", - strerror(errno)); - } - return FAIL; - } - count++; - } else { - if (errno != EINTR) { - break; - } - } - } - /* Make sure proper number of children exited */ - if (count != (nkids * 2)) { - printf("Wrong number of children exited in child group %d, saw %d, expected %d\n", - child_process, count, (nkids * 2)); - if (msgctl(tid, IPC_RMID, 0) < 0) { - printf("msgctl() error: %s\n", strerror(errno)); - } - return FAIL; - } - if (msgctl(id, IPC_RMID, 0) < 0) { - printf("msgctl() failure in child group %d: %s\n", - child_process, strerror(errno)); - return FAIL; - } - return exit_status; -} - -static void term(int sig LTP_ATTRIBUTE_UNUSED) -{ - int i; - - if (procstat == 0) { -#ifdef DEBUG - tst_resm(TINFO, "SIGTERM signal received, test killing kids"); -#endif - for (i = 0; i < nprocs; i++) { - if (pidarray[i] > 0) { - if (kill(pidarray[i], SIGTERM) < 0) { - printf("Kill failed to kill child %d", - i); - exit(FAIL); - } - } - } - return; - } - - if (procstat == 2) { - fflush(stdout); - exit(PASS); - } - - if (tid == -1) { - exit(FAIL); - } - for (i = 0; i < nkids; i++) { - if (rkidarray[i] > 0) - kill(rkidarray[i], SIGTERM); - if (wkidarray[i] > 0) - kill(wkidarray[i], SIGTERM); - } -} - -void setup(void) -{ - int nr_msgqs; - - tst_tmpdir(); - - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - nr_msgqs = get_max_msgqueues(); - if (nr_msgqs < 0) - cleanup(); - - nr_msgqs -= get_used_msgqueues(); - if (nr_msgqs <= 0) { - tst_resm(TBROK, - "Max number of message queues already used, cannot create more."); - cleanup(); - } - - /* - * Since msgmni scales to the memory size, it may reach huge values - * that are not necessary for this test. - * That's why we define NR_MSGQUEUES as a high boundary for it. - */ - MSGMNI = MIN(nr_msgqs, NR_MSGQUEUES); -} - -void cleanup(void) -{ - int status; - -#ifdef DEBUG - tst_resm(TINFO, "Removing the message queue"); -#endif - fflush(stdout); - (void)msgctl(tid, IPC_RMID, NULL); - if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) { - (void)msgctl(tid, IPC_RMID, NULL); - tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed"); - - } - - fflush(stdout); - tst_rmdir(); -} diff --git a/testcases/kernel/syscalls/ipc/msgstress/msgstress03.c b/testcases/kernel/syscalls/ipc/msgstress/msgstress03.c deleted file mode 100755 index aa37d905..00000000 --- a/testcases/kernel/syscalls/ipc/msgstress/msgstress03.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 - * - * 06/30/2001 Port to Linux nsharoff@us.ibm.com - * 11/06/2002 Port to LTP dbarrera@us.ibm.com - */ - -/* - * Get and manipulate a message queue. - * Same as msgstress01 but gets the actual msgmni value under procfs. - */ - -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "ipcmsg.h" -#include "libmsgctl.h" - -char *TCID = "msgstress03"; -int TST_TOTAL = 1; - -#define MAXNPROCS 10000 /*These should be sufficient */ -#define MAXNREPS 10000 /*Else they srewup the system un-necessarily */ - -static key_t keyarray[MAXNPROCS]; -static int pidarray[MAXNPROCS]; -static int tid; -static int MSGMNI, nprocs, nreps; -static int procstat; -static int mykid; - -void setup(void); -void cleanup(void); - -static int dotest(key_t key, int child_process); -static void sig_handler(int signo); - -static char *opt_nprocs; -static char *opt_nreps; - -static option_t options[] = { - {"n:", NULL, &opt_nprocs}, - {"l:", NULL, &opt_nreps}, - {NULL, NULL, NULL}, -}; - -static void usage(void) -{ - printf(" -n Number of processes\n"); - printf(" -l Number of iterations\n"); -} - -int main(int argc, char **argv) -{ - int i, j, ok, pid, free_pids; - int count, status; - struct sigaction act; - - tst_parse_opts(argc, argv, options, usage); - - setup(); - - nreps = MAXNREPS; - nprocs = MSGMNI; - - if (opt_nreps) { - nreps = atoi(opt_nreps); - if (nreps > MAXNREPS) { - tst_resm(TINFO, - "Requested number of iterations too large, " - "setting to Max. of %d", MAXNREPS); - nreps = MAXNREPS; - } - } - - if (opt_nprocs) { - nprocs = atoi(opt_nprocs); - if (nprocs > MSGMNI) { - tst_resm(TINFO, - "Requested number of processes too large, " - "setting to Max. of %d", MSGMNI); - nprocs = MSGMNI; - } - } - - free_pids = tst_get_free_pids(cleanup); - /* Each forked child forks once, take it into account here. */ - if (nprocs * 2 >= free_pids) { - tst_resm(TINFO, - "Requested number of processes higher than limit (%d > %d), " - "setting to %d", nprocs * 2, free_pids, free_pids); - nprocs = free_pids / 2; - } - - srand(getpid()); - tid = -1; - - /* Setup signal handling routine */ - memset(&act, 0, sizeof(act)); - act.sa_handler = sig_handler; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask, SIGTERM); - if (sigaction(SIGTERM, &act, NULL) < 0) { - tst_brkm(TFAIL, NULL, "Sigset SIGTERM failed"); - } - /* Set up array of unique keys for use in allocating message - * queues - */ - for (i = 0; i < nprocs; i++) { - ok = 1; - do { - /* Get random key */ - keyarray[i] = (key_t) rand(); - /* Make sure key is unique and not private */ - if (keyarray[i] == IPC_PRIVATE) { - ok = 0; - continue; - } - for (j = 0; j < i; j++) { - if (keyarray[j] == keyarray[i]) { - ok = 0; - break; - } - ok = 1; - } - } while (ok == 0); - } - - /* Fork a number of processes, each of which will - * create a message queue with one reader/writer - * pair which will read and write a number (iterations) - * of random length messages with specific values. - */ - - for (i = 0; i < nprocs; i++) { - fflush(stdout); - if ((pid = FORK_OR_VFORK()) < 0) { - tst_brkm(TFAIL, - NULL, - "\tFork failed (may be OK if under stress)"); - } - /* Child does this */ - if (pid == 0) { - procstat = 1; - exit(dotest(keyarray[i], i)); - } - pidarray[i] = pid; - } - - count = 0; - while (1) { - if ((wait(&status)) > 0) { - if (status >> 8 != 0) { - tst_brkm(TFAIL, NULL, - "Child exit status = %d", - status >> 8); - } - count++; - } else { - if (errno != EINTR) { - break; - } -#ifdef DEBUG - tst_resm(TINFO, "Signal detected during wait"); -#endif - } - } - /* Make sure proper number of children exited */ - if (count != nprocs) { - tst_brkm(TFAIL, - NULL, - "Wrong number of children exited, Saw %d, Expected %d", - count, nprocs); - } - - tst_resm(TPASS, "Test ran successfully!"); - - cleanup(); - tst_exit(); -} - -static int dotest(key_t key, int child_process) -{ - int id, pid; - int ret, status; - - sighold(SIGTERM); - TEST(msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)); - if (TEST_RETURN < 0) { - printf("msgget() error in child %d: %s\n", - child_process, strerror(TEST_ERRNO)); - return FAIL; - } - tid = id = TEST_RETURN; - sigrelse(SIGTERM); - - fflush(stdout); - if ((pid = FORK_OR_VFORK()) < 0) { - printf("Fork failed (may be OK if under stress)\n"); - TEST(msgctl(tid, IPC_RMID, 0)); - if (TEST_RETURN < 0) { - printf("msgctl() error in cleanup: %s\n", - strerror(TEST_ERRNO)); - } - return FAIL; - } - if (pid == 0) - exit(doreader(key, id, 1, child_process, nreps)); - - mykid = pid; - procstat = 2; - ret = dowriter(key, id, 1, child_process, nreps); - wait(&status); - - if (ret != PASS) - exit(FAIL); - - if ((!WIFEXITED(status) || (WEXITSTATUS(status) != PASS))) - exit(FAIL); - - TEST(msgctl(id, IPC_RMID, 0)); - if (TEST_RETURN < 0) { - printf("msgctl() failed: %s\n", - strerror(TEST_ERRNO)); - return FAIL; - } - return PASS; -} - -static void sig_handler(int signo LTP_ATTRIBUTE_UNUSED) -{ -} - -void setup(void) -{ - int nr_msgqs; - - tst_tmpdir(); - - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - nr_msgqs = get_max_msgqueues(); - if (nr_msgqs < 0) - cleanup(); - - MSGMNI = nr_msgqs - get_used_msgqueues(); - if (MSGMNI > MAXNPROCS) - MSGMNI = MAXNPROCS; - if (MSGMNI <= 0) { - tst_resm(TBROK, - "Max number of message queues already used, cannot create more."); - cleanup(); - } -} - -void cleanup(void) -{ - int status; - -#ifdef DEBUG - tst_resm(TINFO, "Removing the message queue"); -#endif - (void)msgctl(tid, IPC_RMID, NULL); - if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) { - (void)msgctl(tid, IPC_RMID, NULL); - tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed"); - - } - - tst_rmdir(); -} diff --git a/testcases/kernel/syscalls/ipc/msgstress/msgstress04.c b/testcases/kernel/syscalls/ipc/msgstress/msgstress04.c deleted file mode 100755 index b9ebf903..00000000 --- a/testcases/kernel/syscalls/ipc/msgstress/msgstress04.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2002 - * - * 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 - * - * 06/30/2001 Port to Linux nsharoff@us.ibm.com - * 11/11/2002 Port to LTP dbarrera@us.ibm.com - */ - -/* - * Get and manipulate a message queue. - * Same as msgstress02 but gets the actual msgmni value under procfs. - */ - -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "ipcmsg.h" -#include "libmsgctl.h" - -char *TCID = "msgstress04"; -int TST_TOTAL = 1; - -#define MAXNREPS 1000 -#ifndef CONFIG_COLDFIRE -#define MAXNPROCS 1000000 /* This value is set to an arbitrary high limit. */ -#else -#define MAXNPROCS 100000 /* Coldfire can't deal with 1000000 */ -#endif -#define MAXNKIDS 10 -#define DEFNKIDS 2 - -static int maxnkids = MAXNKIDS; /* Used if pid_max is exceeded */ -static key_t keyarray[MAXNPROCS]; -static int pidarray[MAXNPROCS]; -static int rkidarray[MAXNKIDS]; -static int wkidarray[MAXNKIDS]; -static int tid; -static int nprocs, nreps, nkids, MSGMNI; -static int maxnprocs; -static int procstat; - -void setup(void); -void cleanup(void); - -static void term(int); -static int dotest(key_t, int); -static void dotest_iteration(int off); -static void cleanup_msgqueue(int i, int tid); - -static char *opt_maxnprocs; -static char *opt_nkids; -static char *opt_nreps; - -static option_t options[] = { - {"n:", NULL, &opt_maxnprocs}, - {"c:", NULL, &opt_nkids}, - {"l:", NULL, &opt_nreps}, - {NULL, NULL, NULL}, -}; - -static void usage(void) -{ - printf(" -n Number of processes\n"); - printf(" -c Number of read/write child pairs\n"); - printf(" -l Number of iterations\n"); -} - - -int main(int argc, char **argv) -{ - int i, j, ok; - - tst_parse_opts(argc, argv, options, usage); - - setup(); - - nreps = MAXNREPS; - nkids = MAXNKIDS; - - if (opt_nreps) { - nreps = atoi(opt_nreps); - if (nreps > MAXNREPS) { - tst_resm(TINFO, - "Requested number of iterations too large, " - "setting to Max. of %d", MAXNREPS); - nreps = MAXNREPS; - } - } - - if (opt_nkids) { - nkids = atoi(opt_nkids); - if (nkids > MAXNKIDS) { - tst_resm(TINFO, - "Requested number of read/write pairs too " - "large, setting to Max. of %d", MAXNKIDS); - nkids = MAXNKIDS; - } - } - - - if (opt_maxnprocs) { - if (atoi(opt_maxnprocs) > maxnprocs) { - tst_resm(TINFO, - "Requested number of processes too large, " - "setting to Max. of %d", MSGMNI); - } else { - maxnprocs = atoi(opt_maxnprocs); - } - } - - procstat = 0; - srand48((unsigned)getpid() + (unsigned)(getppid() << 16)); - tid = -1; - - /* Setup signal handling routine */ - if (sigset(SIGTERM, term) == SIG_ERR) - tst_brkm(TFAIL, cleanup, "Sigset SIGTERM failed"); - - /* Set up array of unique keys for use in allocating message - * queues - */ - for (i = 0; i < MSGMNI; i++) { - ok = 1; - do { - /* Get random key */ - keyarray[i] = (key_t) lrand48(); - /* Make sure key is unique and not private */ - if (keyarray[i] == IPC_PRIVATE) { - ok = 0; - continue; - } - for (j = 0; j < i; j++) { - if (keyarray[j] == keyarray[i]) { - ok = 0; - break; - } - ok = 1; - } - } while (ok == 0); - } - /* Fork a number of processes, each of which will - * create a message queue with several (nkids) reader/writer - * pairs which will read and write a number (iterations) - * of random length messages with specific values (keys). - * - * We do not fork more than maxnprocs at a time and - * we fork until all the message queues get used. - */ - - if (MSGMNI <= maxnprocs) { - nprocs = MSGMNI; - dotest_iteration(0); - } else { - for (i = 0; i < (MSGMNI / maxnprocs); i++) { - nprocs = maxnprocs; - dotest_iteration(i * maxnprocs); - } - - nprocs = MSGMNI % maxnprocs; - dotest_iteration(i * maxnprocs); - } - - tst_resm(TPASS, "Test ran successfully!"); - - cleanup(); - tst_exit(); -} - -static void dotest_iteration(int off) -{ - key_t key; - int i, count, status; - pid_t pid; - - memset(pidarray, 0, sizeof(pidarray)); - - for (i = 0; i < nprocs; i++) { - key = keyarray[off + i]; - - if ((pid = FORK_OR_VFORK()) < 0) - tst_brkm(TFAIL, cleanup, - "Fork failed (may be OK if under stress)"); - - /* Child does this */ - if (pid == 0) { - procstat = 1; - exit(dotest(key, i)); - } - pidarray[i] = pid; - } - - count = 0; - while (1) { - if ((wait(&status)) > 0) { - if (status >> 8 != PASS) - tst_brkm(TFAIL, cleanup, - "Child exit status = %d", status >> 8); - count++; - } else { - if (errno != EINTR) { - break; - } -#ifdef DEBUG - tst_resm(TINFO, "Signal detected during wait"); -#endif - } - } - /* Make sure proper number of children exited */ - if (count != nprocs) - tst_brkm(TFAIL, cleanup, - "Wrong number of children exited, Saw %d, Expected %d", - count, nprocs); -} - -static void cleanup_msgqueue(int i, int tid) -{ - /* - * Decrease the value of i by 1 because it - * is getting incremented even if the fork - * is failing. - */ - - i--; - /* - * Kill all children & free message queue. - */ - for (; i >= 0; i--) { - (void)kill(rkidarray[i], SIGKILL); - (void)kill(wkidarray[i], SIGKILL); - } - - if (msgctl(tid, IPC_RMID, 0) < 0) { - printf("Msgctl error in cleanup_msgqueue %d\n", errno); - exit(FAIL); - } -} - -static int dotest(key_t key, int child_process) -{ - int id, pid; - int i, count, status, exit_status; - - sighold(SIGTERM); - if ((id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0) { - printf("msgget() error in child %d: %s\n", - child_process, strerror(errno)); - return FAIL; - } - tid = id; - sigrelse(SIGTERM); - - exit_status = PASS; - - for (i = 0; i < nkids; i++) { - if ((pid = FORK_OR_VFORK()) < 0) { - printf("Fork failure in the first child of child group %d\n", - child_process); - cleanup_msgqueue(i, tid); - return FAIL; - } - /* First child does this */ - if (pid == 0) { - procstat = 2; - exit(doreader(key, tid, getpid(), - child_process, nreps)); - } - rkidarray[i] = pid; - if ((pid = FORK_OR_VFORK()) < 0) { - printf("Fork failure in the second child of child group %d\n", - child_process); - /* - * Kill the reader child process - */ - (void)kill(rkidarray[i], SIGKILL); - - cleanup_msgqueue(i, tid); - return FAIL; - } - /* Second child does this */ - if (pid == 0) { - procstat = 2; - exit(dowriter(key, tid, rkidarray[i], - child_process, nreps)); - } - wkidarray[i] = pid; - } - /* Parent does this */ - count = 0; - while (1) { - if ((wait(&status)) > 0) { - if (status >> 8 != PASS) { - printf("Child exit status = %d from child group %d\n", - status >> 8, child_process); - for (i = 0; i < nkids; i++) { - kill(rkidarray[i], SIGTERM); - kill(wkidarray[i], SIGTERM); - } - if (msgctl(tid, IPC_RMID, 0) < 0) { - printf("msgctl() error: %s\n", - strerror(errno)); - } - return FAIL; - } - count++; - } else { - if (errno != EINTR) { - break; - } - } - } - /* Make sure proper number of children exited */ - if (count != (nkids * 2)) { - printf("Wrong number of children exited in child group %d, saw %d, expected %d\n", - child_process, count, (nkids * 2)); - if (msgctl(tid, IPC_RMID, 0) < 0) { - printf("msgctl() error: %s\n", strerror(errno)); - } - return FAIL; - } - if (msgctl(id, IPC_RMID, 0) < 0) { - printf("msgctl() failure in child group %d: %s\n", - child_process, strerror(errno)); - return FAIL; - } - return exit_status; -} - -/* ARGSUSED */ -static void term(int sig LTP_ATTRIBUTE_UNUSED) -{ - int i; - - if (procstat == 0) { -#ifdef DEBUG - tst_resm(TINFO, "SIGTERM signal received, test killing kids"); -#endif - for (i = 0; i < nprocs; i++) { - if (pidarray[i] > 0) { - if (kill(pidarray[i], SIGTERM) < 0) { - tst_resm(TBROK, - "Kill failed to kill child %d", - i); - exit(FAIL); - } - } - } - return; - } - - if (procstat == 2) { - exit(PASS); - } - - if (tid == -1) { - exit(FAIL); - } - for (i = 0; i < nkids; i++) { - if (rkidarray[i] > 0) - kill(rkidarray[i], SIGTERM); - if (wkidarray[i] > 0) - kill(wkidarray[i], SIGTERM); - } -} - -void setup(void) -{ - int nr_msgqs, free_pids; - - tst_tmpdir(); - /* You will want to enable some signal handling so you can capture - * unexpected signals like SIGSEGV. - */ - tst_sig(FORK, DEF_HANDLER, cleanup); - - /* One cavet that hasn't been fixed yet. TEST_PAUSE contains the code to - * fork the test with the -c option. You want to make sure you do this - * before you create your temporary directory. - */ - TEST_PAUSE; - - nr_msgqs = get_max_msgqueues(); - if (nr_msgqs < 0) - tst_brkm(TBROK, cleanup, "get_max_msgqueues() failed"); - - MSGMNI = nr_msgqs - get_used_msgqueues(); - if (MSGMNI <= 0) - tst_brkm(TBROK, cleanup, - "Max number of message queues already used, cannot create more."); - - tst_resm(TINFO, "Found %d available message queues", MSGMNI); - - free_pids = tst_get_free_pids(cleanup); - /* We don't use more than a half of available pids. - * For each child we fork up to 2*maxnkids grandchildren. */ - maxnprocs = (free_pids / 2) / (1 + 2 * maxnkids); - - if (!maxnprocs) - tst_brkm(TBROK, cleanup, "Not enough free pids"); - - tst_resm(TINFO, "Using upto %d pids", free_pids / 2); -} - -void cleanup(void) -{ - int status; - - /* - * Remove the message queue from the system - */ -#ifdef DEBUG - tst_resm(TINFO, "Removing the message queue"); -#endif - (void)msgctl(tid, IPC_RMID, NULL); - if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) { - (void)msgctl(tid, IPC_RMID, NULL); - tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed"); - - } - - tst_rmdir(); -} diff --git a/testcases/kernel/syscalls/ipc/semctl/Makefile b/testcases/kernel/syscalls/ipc/semctl/Makefile index 42d00f1c..aa11e700 100755 --- a/testcases/kernel/syscalls/ipc/semctl/Makefile +++ b/testcases/kernel/syscalls/ipc/semctl/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpipc ltpnewipc +LTPLIBS = ipc newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl01.c b/testcases/kernel/syscalls/ipc/semctl/semctl01.c index ff691adf..92a36bff 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl01.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl01.c @@ -3,8 +3,6 @@ * Copyright (c) International Business Machines Corp., 2001 */ /*\ - * [Description] - * * Test the 13 possible semctl() commands */ diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl02.c b/testcases/kernel/syscalls/ipc/semctl/semctl02.c index 3134dde5..6ca63472 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl02.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl02.c @@ -5,8 +5,6 @@ * 03/2001 - Written by Wayne Boyer */ /*\ - * [Description] - * * Test for semctl() EACCES error. */ diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl03.c b/testcases/kernel/syscalls/ipc/semctl/semctl03.c index f9f335e4..11530b20 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl03.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl03.c @@ -6,8 +6,6 @@ * 03/2001 - Written by Wayne Boyer */ /*\ - * [Description] - * * Test for semctl() EINVAL and EFAULT errors */ diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl04.c b/testcases/kernel/syscalls/ipc/semctl/semctl04.c index 015850a3..f6fa361b 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl04.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl04.c @@ -6,8 +6,6 @@ * 03/2001 - Written by Wayne Boyer */ /*\ - * [Description] - * * Test for semctl() EPERM error * * Runs IPC_SET and IPC_RMID from unprivileged child process. diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl05.c b/testcases/kernel/syscalls/ipc/semctl/semctl05.c index 69df0875..5feee008 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl05.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl05.c @@ -7,8 +7,6 @@ * 03/2001 - Written by Wayne Boyer */ /*\ - * [Description] - * * Test for semctl() ERANGE error */ diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl06.c b/testcases/kernel/syscalls/ipc/semctl/semctl06.c index b3af41fb..b8734c2f 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl06.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl06.c @@ -44,11 +44,6 @@ #define DEBUG 0 -#ifdef UCLINUX -#define _GNU_SOURCE -#include -#endif - #include #include #include @@ -112,7 +107,7 @@ int main(int argc, char **argv) } for (i = 0; i < NPROCS; i++) { - if ((pid = FORK_OR_VFORK()) < 0) { + if ((pid = tst_fork()) < 0) { tst_resm(TFAIL, "\tFork failed (may be OK if under stress)"); @@ -188,7 +183,7 @@ static void dotest(key_t key) } for (i = 0; i < NKIDS; i++) { - if ((pid = FORK_OR_VFORK()) < 0) { + if ((pid = tst_fork()) < 0) { tst_resm(TFAIL, "\tfork failed"); } if (pid == 0) diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl07.c b/testcases/kernel/syscalls/ipc/semctl/semctl07.c index 1b203ef5..588fb243 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl07.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl07.c @@ -10,8 +10,6 @@ * to conflict with other instances of the same test. */ /*\ - * [Description] - * * Basic tests for semctl(). * * - semctl() with IPC_STAT where we check the semid_ds buf content diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl08.c b/testcases/kernel/syscalls/ipc/semctl/semctl08.c index 1878bd49..f4549adf 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl08.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl08.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Viresh Kumar - * - * Description: + */ + +/*\ * Cross verify the _high fields being set to 0 by the kernel. */ + #include "lapi/sembuf.h" #include "lapi/sem.h" #include "tst_test.h" diff --git a/testcases/kernel/syscalls/ipc/semctl/semctl09.c b/testcases/kernel/syscalls/ipc/semctl/semctl09.c index efbc67f3..32b411ef 100755 --- a/testcases/kernel/syscalls/ipc/semctl/semctl09.c +++ b/testcases/kernel/syscalls/ipc/semctl/semctl09.c @@ -4,8 +4,6 @@ * Author: Feiyu Zhu */ /*\ - * [Description] - * * Call semctl() with SEM_INFO flag and check that: * * * The returned index points to a valid SEM by calling SEM_STAT_ANY diff --git a/testcases/kernel/syscalls/ipc/semget/Makefile b/testcases/kernel/syscalls/ipc/semget/Makefile index b1201281..d1e778f9 100755 --- a/testcases/kernel/syscalls/ipc/semget/Makefile +++ b/testcases/kernel/syscalls/ipc/semget/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/semget/semget01.c b/testcases/kernel/syscalls/ipc/semget/semget01.c index 872acabd..3c05517e 100755 --- a/testcases/kernel/syscalls/ipc/semget/semget01.c +++ b/testcases/kernel/syscalls/ipc/semget/semget01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This case checks that semget() correclty creates a semaphore set. */ diff --git a/testcases/kernel/syscalls/ipc/semget/semget02.c b/testcases/kernel/syscalls/ipc/semget/semget02.c index 4273c84c..d381bbd6 100755 --- a/testcases/kernel/syscalls/ipc/semget/semget02.c +++ b/testcases/kernel/syscalls/ipc/semget/semget02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This basic error handing of the semget syscall. * * - EACCES - a semaphore set exists for key, but the calling process does not diff --git a/testcases/kernel/syscalls/ipc/semget/semget05.c b/testcases/kernel/syscalls/ipc/semget/semget05.c index dd9a6285..0e41a152 100755 --- a/testcases/kernel/syscalls/ipc/semget/semget05.c +++ b/testcases/kernel/syscalls/ipc/semget/semget05.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test for ENOSPC error. * * ENOSPC - a semaphore set exceed the maximum number of semaphore sets(SEMMNI) @@ -43,7 +41,7 @@ static void setup(void) SAFE_FILE_SCANF("/proc/sys/kernel/sem", "%*d %*d %*d %d", &maxsems); /* Prevent timeout due to high semaphore array limit */ - tst_set_max_runtime(maxsems / 200); + tst_set_runtime(maxsems / 200); sem_id_arr = SAFE_MALLOC((maxsems - used_cnt) * sizeof(int)); for (num = 0; num < maxsems - used_cnt; num++) { diff --git a/testcases/kernel/syscalls/ipc/semop/Makefile b/testcases/kernel/syscalls/ipc/semop/Makefile index 43afffb3..5d082e91 100755 --- a/testcases/kernel/syscalls/ipc/semop/Makefile +++ b/testcases/kernel/syscalls/ipc/semop/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/semop/semop04.c b/testcases/kernel/syscalls/ipc/semop/semop04.c index 1f49e774..f1fa549e 100644 --- a/testcases/kernel/syscalls/ipc/semop/semop04.c +++ b/testcases/kernel/syscalls/ipc/semop/semop04.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Creates a semaphore and two processes. The processes * each go through a loop where they semdown, delay for a * random amount of time, and semup, so they will almost diff --git a/testcases/kernel/syscalls/ipc/shmat/.gitignore b/testcases/kernel/syscalls/ipc/shmat/.gitignore index 2b972f8f..5600b274 100755 --- a/testcases/kernel/syscalls/ipc/shmat/.gitignore +++ b/testcases/kernel/syscalls/ipc/shmat/.gitignore @@ -1,3 +1,4 @@ /shmat01 /shmat02 /shmat03 +/shmat04 diff --git a/testcases/kernel/syscalls/ipc/shmat/Makefile b/testcases/kernel/syscalls/ipc/shmat/Makefile index 6b2b26d0..baa2a48e 100755 --- a/testcases/kernel/syscalls/ipc/shmat/Makefile +++ b/testcases/kernel/syscalls/ipc/shmat/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/shmat/shmat02.c b/testcases/kernel/syscalls/ipc/shmat/shmat02.c index 53cb6f54..3ad1fd08 100755 --- a/testcases/kernel/syscalls/ipc/shmat/shmat02.c +++ b/testcases/kernel/syscalls/ipc/shmat/shmat02.c @@ -44,20 +44,7 @@ static struct test_case_t { static void verify_shmat(struct test_case_t *tc) { - void *addr; - - addr = shmat(*tc->shmid, *tc->shmaddr, 0); - if (addr != (void *)-1) { - tst_res(TFAIL, "shmat() succeeded unexpectedly"); - return; - } - - if (errno == tc->exp_err) { - tst_res(TPASS | TERRNO, "shmat() failed as expected"); - } else { - tst_res(TFAIL | TERRNO, "shmat() failed unexpectedly, expected: %s", - tst_strerrno(tc->exp_err)); - } + TST_EXP_FAIL_PTR_VOID(shmat(*tc->shmid, *tc->shmaddr, 0), tc->exp_err); } static void do_shmat(unsigned int n) diff --git a/testcases/kernel/syscalls/ipc/shmat/shmat04.c b/testcases/kernel/syscalls/ipc/shmat/shmat04.c new file mode 100644 index 00000000..7b7b802c --- /dev/null +++ b/testcases/kernel/syscalls/ipc/shmat/shmat04.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC + * Author: Michal Hocko + * LTP port: Andrea Cervesato + */ + +/*\ + * When debugging issues with a workload using SysV shmem, Michal Hocko has + * come up with a reproducer that shows how a series of mprotect() + * operations can result in an elevated shm_nattch and thus leak of the + * resource. + * + * The problem is caused by wrong assumptions in vma_merge() commit + * 714965ca8252 ("mm/mmap: start distinguishing if vma can be removed in + * mergeability test"). The shmem vmas have a vma_ops->close callback + * that decrements shm_nattch, and we remove the vma without calling it. + * + * Patch: https://lore.kernel.org/all/20240222215930.14637-2-vbabka@suse.cz/ + */ + +#include "tst_test.h" +#include "tst_safe_sysv_ipc.h" +#include "libnewipc.h" + +static int segment_id = -1; +static int key_id; +static int page_size; +static size_t segment_size; + +static void run(void) +{ + struct shmid_ds shmid_ds; + void *sh_mem; + + segment_id = SAFE_SHMGET( + key_id, + segment_size, + IPC_CREAT | IPC_EXCL | 0600); + + sh_mem = SAFE_SHMAT(segment_id, NULL, 0); + + tst_res(TINFO, "Attached at %p. key: %d - size: %lu", + sh_mem, segment_id, segment_size); + + SAFE_SHMCTL(segment_id, IPC_STAT, &shmid_ds); + + tst_res(TINFO, "Number of attaches: %lu", shmid_ds.shm_nattch); + + SAFE_MPROTECT(sh_mem + page_size, page_size, PROT_NONE); + SAFE_MPROTECT(sh_mem, 2 * page_size, PROT_WRITE); + + SAFE_SHMCTL(segment_id, IPC_STAT, &shmid_ds); + + tst_res(TINFO, "Number of attaches: %lu", shmid_ds.shm_nattch); + tst_res(TINFO, "Delete attached memory"); + + SAFE_SHMDT(sh_mem); + SAFE_SHMCTL(segment_id, IPC_STAT, &shmid_ds); + + tst_res(TINFO, "Number of attaches: %lu", shmid_ds.shm_nattch); + + SAFE_SHMCTL(segment_id, IPC_RMID, NULL); + segment_id = -1; + + TST_EXP_EQ_LU(shmid_ds.shm_nattch, 0); +} + +static void setup(void) +{ + key_id = GETIPCKEY() + 1; + page_size = getpagesize(); + + tst_res(TINFO, "Key id: %d", key_id); + tst_res(TINFO, "Page size: %d", page_size); + + segment_size = 3 * page_size; +} + +static void cleanup(void) +{ + if (segment_id != -1) + SAFE_SHMCTL(segment_id, IPC_RMID, NULL); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .tags = (const struct tst_tag[]) { + {"linux-git", "fc0c8f9089c2"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/ipc/shmctl/Makefile b/testcases/kernel/syscalls/ipc/shmctl/Makefile index f79ffa6d..d4278dcd 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/Makefile +++ b/testcases/kernel/syscalls/ipc/shmctl/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc shmctl05: CFLAGS += -pthread shmctl05: LDLIBS += -lrt diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c index e631b7b2..e86cd710 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that shmctl() IPC_STAT and SHM_STAT reports correct data. * * The shm_nattach is excercised by: @@ -244,9 +242,9 @@ static int get_shm_idx_from_id(int shm_id) static void setup(void) { - ctime_min = tst_get_fs_timestamp(); + ctime_min = tst_fs_timestamp_start(); shm_id = SAFE_SHMGET(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | SHM_RW); - ctime_max = tst_get_fs_timestamp(); + ctime_max = tst_fs_timestamp_end(); shm_idx = get_shm_idx_from_id(shm_id); diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl02.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl02.c index 5c6dc533..0b12944c 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl02.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl02.c @@ -9,8 +9,6 @@ */ /*\ - * [Description] - * * Test for EACCES, EFAULT and EINVAL errors. * * * EACCES - segment has no read or write permissions diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl03.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl03.c index 199ee410..a3291c37 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl03.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Call shmctl() with IPC_INFO flag and check that the data are consistent with * /proc/sys/kernel/shm*. */ diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl04.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl04.c index b244026c..da73863d 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl04.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Call shmctl() with SHM_INFO flag and check that: * * * The returned index points to a valid SHM by calling SHM_STAT_ANY diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c index ca668aaf..6d2415bb 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Regression test for commit * 3f05317d9889 (ipc/shm: fix use-after-free of shm file via remap_file_pages()). * @@ -106,7 +104,7 @@ static void cleanup(void) } static struct tst_test test = { - .max_runtime = 10, + .min_runtime = 40, .setup = setup, .test_all = do_test, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl06.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl06.c index 92aaa38e..63e7f843 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl06.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl06.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Cross verify the _high fields being set to 0 by the kernel. */ diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl07.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl07.c index 15af7b45..1cc07ead 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl07.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl07.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test for a SHM_LOCK and SHM_UNLOCK. */ diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl08.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl08.c index bb2abca4..f72f9f9e 100755 --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl08.c +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl08.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test for a SHM_SET. * * The test clears the group and others bits from the shm_perm.mode and checks diff --git a/testcases/kernel/syscalls/ipc/shmdt/Makefile b/testcases/kernel/syscalls/ipc/shmdt/Makefile index b1201281..d1e778f9 100755 --- a/testcases/kernel/syscalls/ipc/shmdt/Makefile +++ b/testcases/kernel/syscalls/ipc/shmdt/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/shmdt/shmdt01.c b/testcases/kernel/syscalls/ipc/shmdt/shmdt01.c index 551daac9..0a6d4096 100755 --- a/testcases/kernel/syscalls/ipc/shmdt/shmdt01.c +++ b/testcases/kernel/syscalls/ipc/shmdt/shmdt01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This case check whether we get SIGSEGV when write a value to a detached * shared memory resource. */ diff --git a/testcases/kernel/syscalls/ipc/shmdt/shmdt02.c b/testcases/kernel/syscalls/ipc/shmdt/shmdt02.c index 782c6f48..4bdc6eb2 100755 --- a/testcases/kernel/syscalls/ipc/shmdt/shmdt02.c +++ b/testcases/kernel/syscalls/ipc/shmdt/shmdt02.c @@ -4,12 +4,10 @@ */ /*\ - * [Description] - * * Tests basic error handing of the shmdt syscall. * - * -EINVAL there is no shared memory segment attached at shmaddr. - * -EINVAL shmaddr is not aligned on a page boundary. + * - EINVAL there is no shared memory segment attached at shmaddr. + * - EINVAL shmaddr is not aligned on a page boundary. */ #include diff --git a/testcases/kernel/syscalls/ipc/shmget/Makefile b/testcases/kernel/syscalls/ipc/shmget/Makefile index b1201281..d1e778f9 100755 --- a/testcases/kernel/syscalls/ipc/shmget/Makefile +++ b/testcases/kernel/syscalls/ipc/shmget/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../../.. -LTPLIBS = ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/ipc/shmget/shmget02.c b/testcases/kernel/syscalls/ipc/shmget/shmget02.c index 8168803a..8a0961b0 100755 --- a/testcases/kernel/syscalls/ipc/shmget/shmget02.c +++ b/testcases/kernel/syscalls/ipc/shmget/shmget02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test for ENOENT, EEXIST, EINVAL, EACCES, EPERM errors. * * - ENOENT - No segment exists for the given key and IPC_CREAT was not specified. @@ -37,8 +35,6 @@ #include "libnewipc.h" #include "lapi/shm.h" -#define CONFIG_HUGETLBFS "CONFIG_HUGETLBFS" - static int shm_id = -1; static key_t shmkey, shmkey1; static struct passwd *pw; @@ -66,10 +62,7 @@ static struct tcase { static int get_hugetlb_exp_error(void) { long tmp; - struct tst_kconfig_var kconfig = { - .id = CONFIG_HUGETLBFS, - .id_len = sizeof(CONFIG_HUGETLBFS)-1, - }; + struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_HUGETLBFS"); tst_kconfig_read(&kconfig, 1); diff --git a/testcases/kernel/syscalls/ipc/shmget/shmget03.c b/testcases/kernel/syscalls/ipc/shmget/shmget03.c index 8b157e43..092f2170 100755 --- a/testcases/kernel/syscalls/ipc/shmget/shmget03.c +++ b/testcases/kernel/syscalls/ipc/shmget/shmget03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test for ENOSPC error. * * ENOSPC - All possible shared memory segments have been taken (SHMMNI). diff --git a/testcases/kernel/syscalls/ipc/shmget/shmget04.c b/testcases/kernel/syscalls/ipc/shmget/shmget04.c index 0e48b095..2a6a676c 100755 --- a/testcases/kernel/syscalls/ipc/shmget/shmget04.c +++ b/testcases/kernel/syscalls/ipc/shmget/shmget04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test for EACCES error. * * Create a shared memory segment without read or write permissions under diff --git a/testcases/kernel/syscalls/ipc/shmget/shmget05.c b/testcases/kernel/syscalls/ipc/shmget/shmget05.c index 42a11547..d96a9854 100755 --- a/testcases/kernel/syscalls/ipc/shmget/shmget05.c +++ b/testcases/kernel/syscalls/ipc/shmget/shmget05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for shm_next_id. * * shm_next_id specifies desired id for next allocated IPC shared memory. By diff --git a/testcases/kernel/syscalls/ipc/shmget/shmget06.c b/testcases/kernel/syscalls/ipc/shmget/shmget06.c index d91b7b63..f4bd61bc 100755 --- a/testcases/kernel/syscalls/ipc/shmget/shmget06.c +++ b/testcases/kernel/syscalls/ipc/shmget/shmget06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for shm_next_id. * * When the shared memory segment identifier that shm_next_id stored is already diff --git a/testcases/kernel/syscalls/kcmp/kcmp01.c b/testcases/kernel/syscalls/kcmp/kcmp01.c index 0e7cc7a2..50544f46 100755 --- a/testcases/kernel/syscalls/kcmp/kcmp01.c +++ b/testcases/kernel/syscalls/kcmp/kcmp01.c @@ -1,14 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda + * Copyright (c) Linux Test Project, 2015-2024 */ - /* Description: - * Verify that: - * 1) kcmp returns 0 with two process and two fd refering to the - * same open file - * 2) kcmp doesn't return 0 with two process and two fd not - * refering to the same open file +/*\ + * Verify that + * + * 1. kcmp() returns 0 with two process and two file descriptors refering to the + * same open file + * 2. kcmp() doesn't return 0 with two process and two file descriptors not + * refering to the same open file */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/kcmp/kcmp02.c b/testcases/kernel/syscalls/kcmp/kcmp02.c index 076b4a72..cf76e2cc 100755 --- a/testcases/kernel/syscalls/kcmp/kcmp02.c +++ b/testcases/kernel/syscalls/kcmp/kcmp02.c @@ -1,16 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015 Cedric Hnyda + * Copyright (c) Linux Test Project, 2015-2024 */ - /* Description: - * Verify that: - * 1) kcmp fails with bad pid - * 2) kcmp fails with invalid flag - * 3) kcmp fails with invalid flag - * 4) kcmp fails with invalid flag - * 5) kcmp fails with invalid flag - * 6) kcmp fails with invalid fd +/*\ + * Verify that, kcmp() returns -1 and sets errno to + * + * 1. ESRCH if pid does not exist + * 2. EINVAL if type is invalid (KCMP_TYPES + 1) + * 3. EINVAL if type is invalid (-1) + * 4. EINVAL if type is invalid (INT_MIN) + * 5. EINVAL if type is invalid (INT_MAX) + * 6. EBADF if file descriptor is invalid */ #define _GNU_SOURCE @@ -33,20 +35,22 @@ static int fd_fake = -1; #include #include +#define TYPE_DESC(x) .type = x, .desc = #x static struct test_case { int *pid1; int *pid2; int type; + char *desc; int *fd1; int *fd2; int exp_errno; } test_cases[] = { - {&pid1, &pid_unused, KCMP_FILE, &fd1, &fd2, ESRCH}, - {&pid1, &pid1, KCMP_TYPES + 1, &fd1, &fd2, EINVAL}, - {&pid1, &pid1, -1, &fd1, &fd2, EINVAL}, - {&pid1, &pid1, INT_MIN, &fd1, &fd2, EINVAL}, - {&pid1, &pid1, INT_MAX, &fd1, &fd2, EINVAL}, - {&pid1, &pid1, KCMP_FILE, &fd1, &fd_fake, EBADF} + {&pid1, &pid_unused, TYPE_DESC(KCMP_FILE), &fd1, &fd2, ESRCH}, + {&pid1, &pid1, TYPE_DESC(KCMP_TYPES + 1), &fd1, &fd2, EINVAL}, + {&pid1, &pid1, TYPE_DESC(-1), &fd1, &fd2, EINVAL}, + {&pid1, &pid1, TYPE_DESC(INT_MIN), &fd1, &fd2, EINVAL}, + {&pid1, &pid1, TYPE_DESC(INT_MAX), &fd1, &fd2, EINVAL}, + {&pid1, &pid1, TYPE_DESC(KCMP_FILE), &fd1, &fd_fake, EBADF} }; static void setup(void) @@ -69,24 +73,11 @@ static void cleanup(void) static void verify_kcmp(unsigned int n) { - struct test_case *test = &test_cases[n]; + struct test_case *tc = &test_cases[n]; - TEST(kcmp(*(test->pid1), *(test->pid2), test->type, - *(test->fd1), *(test->fd2))); - - if (TST_RET != -1) { - tst_res(TFAIL, "kcmp() succeeded unexpectedly"); - return; - } - - if (test->exp_errno == TST_ERR) { - tst_res(TPASS | TTERRNO, "kcmp() returned the expected value"); - return; - } - - tst_res(TFAIL | TTERRNO, - "kcmp() got unexpected return value: expected: %d - %s", - test->exp_errno, tst_strerrno(test->exp_errno)); + TST_EXP_FAIL(kcmp(*(tc->pid1), *(tc->pid2), tc->type, + *(tc->fd1), *(tc->fd2)), tc->exp_errno, "kcmp(%d,%d,%s,%d,%d)", + *tc->pid1, *tc->pid2, tc->desc, *tc->fd1, *tc->fd2); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/kcmp/kcmp03.c b/testcases/kernel/syscalls/kcmp/kcmp03.c index 7af5cb15..a3133335 100755 --- a/testcases/kernel/syscalls/kcmp/kcmp03.c +++ b/testcases/kernel/syscalls/kcmp/kcmp03.c @@ -1,17 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2016 Xiao Yang + * Copyright (c) Linux Test Project, 2016-2024 */ - /* - * Testname: kcmp03.c +/*\ + * Verify that, kcmp() returns 0 if the processes * - * Description: - * 1) kcmp() returns 0 if the processes share the same file system information. - * 2) kcmp() returns 0 if the processes share I/O context. - * 3) kcmp() returns 0 if the processes share the same list of System V - * semaphore undo operations. - * 4) kcmp() returns 0 if the processes share the same address space. + * 1. share the same file system information + * 2. share I/O context + * 3. share the same list of System V semaphore undo operations + * 4. share the same address space */ #define _GNU_SOURCE @@ -29,14 +28,16 @@ static int pid1; static int pid2; static void *stack; +#define ARGS(x, y) .clone_type = x, .kcmp_type = y, .desc = #x ", " #y static struct tcase { int clone_type; int kcmp_type; + char *desc; } tcases[] = { - {CLONE_VM, KCMP_VM}, - {CLONE_FS, KCMP_FS}, - {CLONE_IO, KCMP_IO}, - {CLONE_SYSVSEM, KCMP_SYSVSEM} + {ARGS(CLONE_VM, KCMP_VM)}, + {ARGS(CLONE_FS, KCMP_FS)}, + {ARGS(CLONE_IO, KCMP_IO)}, + {ARGS(CLONE_SYSVSEM, KCMP_SYSVSEM)} }; static void setup(void) @@ -52,28 +53,17 @@ static void cleanup(void) static int do_child(void *arg) { pid2 = getpid(); - - TEST(kcmp(pid1, pid2, *(int *)arg, 0, 0)); - if (TST_RET == -1) { - tst_res(TFAIL | TTERRNO, "kcmp() failed unexpectedly"); - return 0; - } - - if (TST_RET == 0) - tst_res(TPASS, "kcmp() returned the expected value"); - else - tst_res(TFAIL, "kcmp() returned the unexpected value"); - + TST_EXP_PASS(kcmp(pid1, pid2, *(int *)arg, 0, 0)); return 0; } static void verify_kcmp(unsigned int n) { int res; - struct tcase *tc = &tcases[n]; pid1 = getpid(); + tst_res(TINFO, "Testing %s", tc->desc); res = ltp_clone(tc->clone_type | SIGCHLD, do_child, &tc->kcmp_type, STACK_SIZE, stack); diff --git a/testcases/kernel/syscalls/keyctl/keyctl01.c b/testcases/kernel/syscalls/keyctl/keyctl01.c index 55e069c6..181e37dd 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl01.c +++ b/testcases/kernel/syscalls/keyctl/keyctl01.c @@ -2,16 +2,17 @@ /* * Copyright (c) Crackerjack Project., 2007 * Copyright (c) 2017 Fujitsu Ltd. - */ - -/* - * Description: This tests the keyctl() syscall - * Manipulate the kernel's key management facility - * + * Copyright (c) Linux Test Project, 2009-2024 * Ported by Manas Kumar Nayak maknayak@in.ibm.com> * Modified by Guangwen Feng */ +/*\ + * Tests the keyctl(2) syscall. + * + * Manipulate the kernel's key management facility. + */ + #include #include diff --git a/testcases/kernel/syscalls/keyctl/keyctl02.c b/testcases/kernel/syscalls/keyctl/keyctl02.c index 35cc2838..3c5a2501 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl02.c +++ b/testcases/kernel/syscalls/keyctl/keyctl02.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Fujitsu Ltd. - * Ported: Guangwen Feng + * Copyright (c) Linux Test Project, 2017-2024 + * Ported: Guangwen Feng */ -/* - * This is a regression test for the race between keyctl_read() and +/*\ + * Regression test for the race between keyctl_read() and * keyctl_revoke(), if the revoke happens between keyctl_read() * checking the validity of a key and the key's semaphore being taken, * then the key type read method will see a revoked key. @@ -14,13 +15,8 @@ * assumes in its read method that there will always be a payload * in a non-revoked key and doesn't check for a NULL pointer. * - * This test can crash the buggy kernel, and the bug was fixed in: - * - * commit b4a1b4f5047e4f54e194681125c74c0aa64d637d - * Author: David Howells - * Date: Fri Dec 18 01:34:26 2015 +0000 - * - * KEYS: Fix race between read and revoke + * Bug was fixed in commit + * b4a1b4f5047e ("KEYS: Fix race between read and revoke") */ #include @@ -29,6 +25,7 @@ #include "tst_safe_pthread.h" #include "tst_test.h" +#include "tst_kconfig.h" #include "lapi/keyctl.h" #define LOOPS 20000 @@ -36,6 +33,7 @@ #define PATH_KEY_COUNT_QUOTA "/proc/sys/kernel/keys/root_maxkeys" static int orig_maxkeys; +static int realtime_kernel; static void *do_read(void *arg) { @@ -86,6 +84,15 @@ static void do_test(void) tst_res(TINFO, "Runtime exhausted, exiting after %d loops", i); break; } + + /* + * Realtime kernel has deferred post-join thread cleanup which + * may result in exhaustion of cgroup thread limit. Add delay + * to limit the maximum number of stale threads to 4000 + * even with CONFIG_HZ=100. + */ + if (realtime_kernel) + usleep(100); } /* @@ -126,8 +133,19 @@ static void do_test(void) static void setup(void) { + unsigned int i; + struct tst_kconfig_var rt_kconfigs[] = { + TST_KCONFIG_INIT("CONFIG_PREEMPT_RT"), + TST_KCONFIG_INIT("CONFIG_PREEMPT_RT_FULL") + }; + SAFE_FILE_SCANF(PATH_KEY_COUNT_QUOTA, "%d", &orig_maxkeys); SAFE_FILE_PRINTF(PATH_KEY_COUNT_QUOTA, "%d", orig_maxkeys + LOOPS + 1); + + tst_kconfig_read(rt_kconfigs, ARRAY_SIZE(rt_kconfigs)); + + for (i = 0; i < ARRAY_SIZE(rt_kconfigs); i++) + realtime_kernel |= rt_kconfigs[i].choice == 'y'; } static void cleanup(void) @@ -140,7 +158,7 @@ static struct tst_test test = { .needs_root = 1, .setup = setup, .cleanup = cleanup, - .max_runtime = 60, + .runtime = 60, .test_all = do_test, .tags = (const struct tst_tag[]) { {"linux-git", "b4a1b4f5047e"}, diff --git a/testcases/kernel/syscalls/keyctl/keyctl03.c b/testcases/kernel/syscalls/keyctl/keyctl03.c index 9d7b9a0b..da97b24c 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl03.c +++ b/testcases/kernel/syscalls/keyctl/keyctl03.c @@ -1,19 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Fujitsu Ltd. - * Ported: Guangwen Feng + * Copyright (c) Linux Test Project, 2017-2024 + * Ported: Guangwen Feng */ -/* - * This regression test can crash the buggy kernel, - * and the bug was fixed in: - * - * commit f05819df10d7b09f6d1eb6f8534a8f68e5a4fe61 - * Author: David Howells - * Date: Thu Oct 15 17:21:37 2015 +0100 - * - * KEYS: Fix crash when attempt to garbage collect - * an uninstantiated keyring +/*\ + * Regression test for commit + * f05819df10d7 ("KEYS: Fix crash when attempt to garbage collect an uninstantiated keyring") */ #include diff --git a/testcases/kernel/syscalls/keyctl/keyctl04.c b/testcases/kernel/syscalls/keyctl/keyctl04.c index 1fed23ca..aff95a57 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl04.c +++ b/testcases/kernel/syscalls/keyctl/keyctl04.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Google, Inc. + * Copyright (c) Linux Test Project, 2017-2024 */ -/* +/*\ * Regression test for commit c9f838d104fe ("KEYS: fix * keyctl_set_reqkey_keyring() to not leak thread keyrings"), a.k.a. * CVE-2017-7472. This bug could be used to exhaust kernel memory, though it diff --git a/testcases/kernel/syscalls/keyctl/keyctl05.c b/testcases/kernel/syscalls/keyctl/keyctl05.c index 7d7c076c..cd738fb3 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl05.c +++ b/testcases/kernel/syscalls/keyctl/keyctl05.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Google, Inc. + * Copyright (c) Linux Test Project, 2017-2024 */ -/* +/*\ * Regression test for commit 63a0b0509e70 ("KEYS: fix freeing uninitialized * memory in key_update()"). Try to reproduce the crash in two different ways: * @@ -31,6 +32,8 @@ #include "tst_test.h" #include "lapi/keyctl.h" +#define MODULE "dns_resolver" + /* * A valid payload for the "asymmetric" key type. This is an x509 certificate * in DER format, generated using: @@ -190,6 +193,9 @@ static void test_update_setperm_race(void) static void setup(void) { + /* There is no way to trigger automatic dns_resolver module loading. */ + tst_cmd((const char*[]){"modprobe", MODULE, NULL}, NULL, NULL, 0); + fips_enabled = tst_fips_enabled(); } @@ -198,8 +204,12 @@ static void do_test(unsigned int i) /* * We need to pass check in dns_resolver_preparse(), * give it dummy server list request. + * From v6.8-rc1 commit acc657692aed438e9931438f8c923b2b107aebf9: + * the incoming data for add_key() sysdall should be not less than 6 + * bytes, because struct dns_server_list_v1_header is 6 bytes. + * The minimum payload will be tested here for boundary testing. */ - static char dns_res_payload[] = { 0x00, 0x00, 0x01, 0xff, 0x00 }; + static char dns_res_payload[] = { 0x00, 0x00, 0x01, 0xff, 0x00, 0x00 }; switch (i) { case 0: @@ -207,7 +217,7 @@ static void do_test(unsigned int i) x509_cert, sizeof(x509_cert)); break; case 1: - test_update_nonupdatable("dns_resolver", dns_res_payload, + test_update_nonupdatable(MODULE, dns_res_payload, sizeof(dns_res_payload)); break; case 2: @@ -217,12 +227,14 @@ static void do_test(unsigned int i) } static struct tst_test test = { + .needs_root = 1, .tcnt = 3, .setup = setup, .test = do_test, .forks_child = 1, .tags = (const struct tst_tag[]) { {"linux-git", "63a0b0509e70"}, + {"linux-git", "acc657692aed"}, {} } }; diff --git a/testcases/kernel/syscalls/keyctl/keyctl06.c b/testcases/kernel/syscalls/keyctl/keyctl06.c index f76a85ff..3e23e914 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl06.c +++ b/testcases/kernel/syscalls/keyctl/keyctl06.c @@ -1,19 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Google, Inc. + * Copyright (c) Linux Test Project, 2017-2024 */ -/* - * Regression test for: +/*\ + * Regression test for commit: * - * commit e645016abc80 ("KEYS: fix writing past end of user-supplied buffer - * in keyring_read()"). + * e645016abc80 ("KEYS: fix writing past end of user-supplied buffer in keyring_read()") * * as well as its follow-on fix: * - * commit 3239b6f29bdf ("KEYS: return full count in keyring_read() if - * buffer is too small") - * + * commit 3239b6f29bdf ("KEYS: return full count in keyring_read() if buffer is too small") */ #include diff --git a/testcases/kernel/syscalls/keyctl/keyctl07.c b/testcases/kernel/syscalls/keyctl/keyctl07.c index d9e20db5..e4e0ff57 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl07.c +++ b/testcases/kernel/syscalls/keyctl/keyctl07.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Google, Inc. + * Copyright (c) Linux Test Project, 2018-2024 */ -/* +/*\ * Regression test for commit 37863c43b2c6 ("KEYS: prevent KEYCTL_READ on * negative key"). This is CVE-2017-12192. */ diff --git a/testcases/kernel/syscalls/keyctl/keyctl08.c b/testcases/kernel/syscalls/keyctl/keyctl08.c index be4b23b1..e038123e 100755 --- a/testcases/kernel/syscalls/keyctl/keyctl08.c +++ b/testcases/kernel/syscalls/keyctl/keyctl08.c @@ -1,10 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Richard Palethorpe + * Copyright (c) Linux Test Project, 2019-2024 */ -/* Check for CVE-2016-9604; that keys beginning with "." are disallowed. + +/*\ + * Test for CVE-2016-9604, checks that keys beginning with "." are disallowed. * - * See commit ee8f844e3c5a73b999edf733df1c529d6503ec2f + * See commit + * ee8f844e3c5a ("KEYS: Disallow keyrings beginning with '.' to be joined as session keyrings") */ #include diff --git a/testcases/kernel/syscalls/keyctl/keyctl09.c b/testcases/kernel/syscalls/keyctl/keyctl09.c index cfd5f7e5..00ff61da 100644 --- a/testcases/kernel/syscalls/keyctl/keyctl09.c +++ b/testcases/kernel/syscalls/keyctl/keyctl09.c @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2022 Google, Inc. + * Copyright (c) Linux Test Project, 2023 */ /*\ - * [Description] - * * Test that encrypted keys can be instantiated using user-provided decrypted * data that is hex-ascii encoded. */ diff --git a/testcases/kernel/syscalls/kill/.gitignore b/testcases/kernel/syscalls/kill/.gitignore index 810ed020..f73696b9 100755 --- a/testcases/kernel/syscalls/kill/.gitignore +++ b/testcases/kernel/syscalls/kill/.gitignore @@ -2,9 +2,7 @@ /kill03 /kill05 /kill06 -/kill07 /kill08 -/kill09 /kill10 /kill11 /kill12 diff --git a/testcases/kernel/syscalls/kill/Makefile b/testcases/kernel/syscalls/kill/Makefile index 0cc064b3..dfe5c305 100755 --- a/testcases/kernel/syscalls/kill/Makefile +++ b/testcases/kernel/syscalls/kill/Makefile @@ -3,11 +3,10 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpipc ltpnewipc +LTPLIBS = newipc include $(top_srcdir)/include/mk/testcases.mk -kill07: LTPLDLIBS = -lltpipc kill05: LTPLDLIBS = -lltpnewipc include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/kill/kill02.c b/testcases/kernel/syscalls/kill/kill02.c index 7ae2427c..776540b4 100755 --- a/testcases/kernel/syscalls/kill/kill02.c +++ b/testcases/kernel/syscalls/kill/kill02.c @@ -203,51 +203,23 @@ void cleanup(); char *TCID = "kill02"; int TST_TOTAL = 2; -#ifdef UCLINUX -static char *argv0; -void childA_rout_uclinux(); -void childB_rout_uclinux(); -#endif - int main(int ac, char **av) { int lc; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - argv0 = av[0]; - - maybe_run_child(&childA_rout_uclinux, "nd", 1, &pipeA_fd[1]); - maybe_run_child(&childB_rout_uclinux, "nd", 2, &pipeB_fd[1]); - maybe_run_child(&child1_rout, "ndddddd", 3, &pipe1_fd[1], &pipe2_fd[1], - &pipeA_fd[0], &pipeA_fd[1], &pipeB_fd[0], &pipeB_fd[1]); - maybe_run_child(&child2_rout, "nd", 4, &pipe2_fd[1]); -#endif - setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; - if ((pid1 = FORK_OR_VFORK()) > 0) { - if ((pid2 = FORK_OR_VFORK()) > 0) { + if ((pid1 = tst_fork()) > 0) { + if ((pid2 = tst_fork()) > 0) { (void)parent_rout(); } else if (pid2 == 0) { -#ifdef UCLINUX - if (self_exec(argv0, "nd", 4, pipe2_fd[1]) < 0) { - if (kill(pid1, SIGKILL) == -1 - && errno != ESRCH) { - tst_resm(TWARN, - "Child process may not have been killed."); - } - tst_brkm(TBROK | TERRNO, cleanup, - "fork failed"); - } -#else (void)child2_rout(); -#endif } else { /* * The second fork failed kill the first child. @@ -264,17 +236,7 @@ int main(int ac, char **av) /* * This is child 1. */ -#ifdef UCLINUX - if (self_exec - (argv0, "ndddddd", 3, pipe1_fd[1], pipe2_fd[1], - pipeA_fd[0], pipeA_fd[1], pipeB_fd[0], - pipeB_fd[1]) < 0) { - tst_brkm(TBROK | TERRNO, cleanup, - "self_exec() failed"); - } -#else (void)child1_rout(); -#endif } else { /* * Fork failed. @@ -452,22 +414,14 @@ void child1_rout(void) /* * Create children A & B. */ - if ((pidA = FORK_OR_VFORK()) > 0) { + if ((pidA = tst_fork()) > 0) { /* * This is the parent(child1), fork again to create child B. */ - if ((pidB = FORK_OR_VFORK()) == 0) { + + if ((pidB = tst_fork()) == 0) { /* This is child B. */ -#ifdef UCLINUX - if (self_exec(argv0, "nd", 2, pipeB_fd[1]) < 0) { - tst_brkm(TBROK | TERRNO, NULL, - "self_exec() failed"); - (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); - exit(0); - } -#else (void)childB_rout(); -#endif } else if (pidB == -1) { @@ -485,16 +439,7 @@ void child1_rout(void) else if (pidA == 0) { /* This is child A. */ -#ifdef UCLINUX - if (self_exec(argv0, "nd", 1, pipeA_fd[1]) < 0) { - tst_brkm(TBROK | TERRNO, NULL, "self_exec() failed"); - (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); - exit(0); - } -#else (void)childA_rout(); -#endif - } else if (pidA == -1) { @@ -625,24 +570,6 @@ void childA_rout(void) exit(0); } /*End of childA_rout */ -#ifdef UCLINUX -/******************************************************************************* - * This is the routine for child A after self_exec - ******************************************************************************/ -void childA_rout_uclinux(void) -{ - /* Setup the signal handler again */ - if (signal(SIGUSR1, usr1_rout) == SIG_ERR) { - tst_brkm(TBROK, NULL, - "Could not set to catch the childrens signal."); - (void)write(pipeA_fd[1], CHAR_SET_FAILED, 1); - exit(0); - } - - childA_rout(); -} -#endif - /******************************************************************************* * This is the routine for child B, which should not receive the parents signal. ******************************************************************************/ @@ -667,24 +594,6 @@ void childB_rout(void) exit(0); } -#ifdef UCLINUX -/******************************************************************************* - * This is the routine for child B after self_exec - ******************************************************************************/ -void childB_rout_uclinux(void) -{ - /* Setup the signal handler again */ - if (signal(SIGUSR1, usr1_rout) == SIG_ERR) { - tst_brkm(TBROK, NULL, - "Could not set to catch the childrens signal."); - (void)write(pipeB_fd[1], CHAR_SET_FAILED, 1); - exit(0); - } - - childB_rout(); -} -#endif - /******************************************************************************* * This routine sets up the interprocess communication pipes, signal handling, * and process group information. diff --git a/testcases/kernel/syscalls/kill/kill03.c b/testcases/kernel/syscalls/kill/kill03.c index 7d307067..8d91cc61 100755 --- a/testcases/kernel/syscalls/kill/kill03.c +++ b/testcases/kernel/syscalls/kill/kill03.c @@ -1,15 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * - * 1) kill() fails with errno set to EINVAL if given an invalid signal. - * 2) kill() fails with errno set to ESRCH if given a non-existent pid. - * 3) kill() fails with errno set to ESRCH if the given pid is INT_MIN. - * - * HISTORY * 07/2001 Ported by Wayne Boyer */ +/*\ + * Verify that kill(2) fails with the correct error codes: + * + * - EINVAL if given an invalid signal. + * - ESRCH if given a non-existent pid. + * - ESRCH if the given pid is INT_MIN. + */ + #include #include #include diff --git a/testcases/kernel/syscalls/kill/kill05.c b/testcases/kernel/syscalls/kill/kill05.c index 8ec71be4..3c317e2f 100755 --- a/testcases/kernel/syscalls/kill/kill05.c +++ b/testcases/kernel/syscalls/kill/kill05.c @@ -2,8 +2,6 @@ /* * Copyright (c) International Business Machines Corp., 2001 * - * Test case to check that kill() fails when passed a pid owned by another user. - * * HISTORY * 07/2001 Ported by Wayne Boyer * @@ -12,9 +10,10 @@ * segfault in case of error with this syscall). * - Fix deletion of IPC memory segment. Segment was not correctly * deleted due to the change of uid during the test. - * - * RESTRICTIONS - * This test must be run as root. + */ + +/*\ + * Test case to check that kill() fails when passed a pid owned by another user. */ #include diff --git a/testcases/kernel/syscalls/kill/kill06.c b/testcases/kernel/syscalls/kill/kill06.c index 42003a68..1386e1b7 100755 --- a/testcases/kernel/syscalls/kill/kill06.c +++ b/testcases/kernel/syscalls/kill/kill06.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * + * 07/2001 Ported by Wayne Boyer + */ + +/*\ * Test case to check the basic functionality of kill() when killing an * entire process group with a negative pid. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer */ #include diff --git a/testcases/kernel/syscalls/kill/kill07.c b/testcases/kernel/syscalls/kill/kill07.c deleted file mode 100755 index c566a0a0..00000000 --- a/testcases/kernel/syscalls/kill/kill07.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ - -/* - * NAME - * kill07.c - * - * DESCRIPTION - * Test case to check that SIGKILL can not be caught. - * - * ALGORITHM - * call setup - * setup some shared memory - * loop if the -i option was given - * set up to catch SIGKILL - * if SIGKILL is caught set the shared memory flag. - * fork a child - * execute the kill system call - * check the return value - * if return value is -1 - * issue a FAIL message, break remaining tests and cleanup - * if we are doing functional testing - * if the process was terminated with the expected signal and the - * signal was not caught. - * issue a PASS message - * otherwise - * issue a FAIL message - * call cleanup - * - * USAGE - * kill07 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * This test should be run as a non-root user. - */ - -#include "test.h" - -#include -#include -#include -#include -#include - -void cleanup(void); -void setup(void); -void sighandler(int sig); -void do_child(void); - -char *TCID = "kill07"; -int TST_TOTAL = 1; -int shmid1; -extern key_t semkey; -int *flag; - -extern int getipckey(); -extern void rm_shm(int); - -#define TEST_SIG SIGKILL - -int main(int ac, char **av) -{ - int lc; - pid_t pid; - int exno, status, nsig, asig, ret; - struct sigaction my_act, old_act; - - tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, ""); -#endif - - setup(); /* global setup */ - - /* The following loop checks looping state if -i option given */ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - /* reset tst_count in case we are looping */ - tst_count = 0; - status = 1; - exno = 1; - my_act.sa_handler = sighandler; - my_act.sa_flags = SA_RESTART; - sigemptyset(&my_act.sa_mask); - - if ((shmid1 = shmget(semkey, (int)getpagesize(), - 0666 | IPC_CREAT)) == -1) { - tst_brkm(TBROK, cleanup, - "Failed to setup shared memory"); - } - - if (*(flag = shmat(shmid1, 0, 0)) == -1) { - tst_brkm(TBROK, cleanup, - "Failed to attatch shared memory:%d", *flag); - } - - *flag = 0; - - /* setup the signal handler */ - ret = sigaction(TEST_SIG, &my_act, &old_act); - - pid = FORK_OR_VFORK(); - if (pid < 0) { - tst_brkm(TBROK, cleanup, "Fork of child failed"); - } else if (pid == 0) { -#ifdef UCLINUX - if (self_exec(av[0], "") < 0) { - tst_brkm(TBROK, cleanup, - "self_exec of child failed"); - } -#else - do_child(); -#endif - } else { - /* sighandler should not catch this signal */ - /* if it does flag will be set to 1 */ - sleep(1); - TEST(kill(pid, TEST_SIG)); - waitpid(pid, &status, 0); - } - - if (TEST_RETURN == -1) { - tst_brkm(TFAIL, cleanup, "%s failed - errno = %d : %s", - TCID, TEST_ERRNO, strerror(TEST_ERRNO)); - } - - /* - * Check to see if the process was terminated with the - * expected signal. - */ - nsig = WTERMSIG(status); - asig = WIFSIGNALED(status); - if ((asig == 0) & (*flag == 1)) { - tst_resm(TFAIL, "SIGKILL was unexpectedly" - " caught"); - } else if ((asig == 1) & (nsig == TEST_SIG)) { - tst_resm(TINFO, "received expected signal %d", - nsig); - tst_resm(TPASS, - "Did not catch signal as expected"); - } else if (nsig) { - tst_resm(TFAIL, - "expected signal %d received %d", - TEST_SIG, nsig); - } else { - tst_resm(TFAIL, "No signals received"); - } - - if (shmdt(flag)) { - tst_brkm(TBROK, cleanup, "shmdt failed "); - } - } - - cleanup(); - tst_exit(); -} - -/* - * sighandler() - try to catch SIGKILL - */ - -void sighandler(int sig) -{ - /* do nothing */ - *flag = 1; - return; -} - -/* - * do_child() - */ -void do_child(void) -{ - int exno = 1; - - sleep(300); - tst_resm(TINFO, "Child never received a signal"); - exit(exno); -} - -/* - * setup() - performs all ONE TIME setup for this test - */ -void setup(void) -{ - - TEST_PAUSE; - - /* - * Create a temporary directory and cd into it. - * This helps to ensure that a unique msgkey is created. - * See libs/libltpipc/libipc.c for more information. - */ - tst_tmpdir(); - - /* get an IPC resource key */ - semkey = getipckey(); - -} - -/* - * cleanup() - performs all the ONE TIME cleanup for this test at completion - * or premature exit. - */ -void cleanup(void) -{ - - /* - * remove the shared memory - */ - rm_shm(shmid1); - - tst_rmdir(); - -} diff --git a/testcases/kernel/syscalls/kill/kill08.c b/testcases/kernel/syscalls/kill/kill08.c index b0826c2e..7c8f95a5 100755 --- a/testcases/kernel/syscalls/kill/kill08.c +++ b/testcases/kernel/syscalls/kill/kill08.c @@ -1,178 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) IBM Corp., 07/2001 Ported by Wayne Boyer + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* - * NAME - * kill08.c - * - * DESCRIPTION - * Test case to check the basic functionality of kill() when kill an - * entire process group. - * - * ALGORITHM - * call setup - * loop if the -i option was given - * fork 5 childeren - * execute the kill system call - * check the return value - * if return value is -1 - * issue a FAIL message, break remaining tests and cleanup - * if we are doing functional testing - * if the processes were terminated with the expected signal. - * issue a PASS message - * otherwise - * issue a FAIL message - * call cleanup - * - * USAGE - * kill08 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * This test should be run as a non-root user. +/*\ + * Test checks the basic functionality of kill() when killing an + * entire process group. */ -#include "test.h" - -#include -#include -#include - -void cleanup(void); -void setup(void); -void do_child(void); - -char *TCID = "kill08"; -int TST_TOTAL = 1; +#include "tst_test.h" #define TEST_SIG SIGKILL -int main(int ac, char **av) +static void run(void) { - int lc; - pid_t pid1, pid2; - int exno, status, nsig, i; + pid_t pid; + int status, nsig, i; - tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, ""); -#endif + /* + * Fork a process and set the process group so that + * it is different from this one. Fork 5 more children. + */ + pid = SAFE_FORK(); + if (!pid) { + setpgrp(); + for (i = 0; i < 5; i++) + if (!SAFE_FORK()) + pause(); - setup(); /* global setup */ - - /* The following loop checks looping state if -i option given */ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - /* reset tst_count in case we are looping */ - tst_count = 0; - status = 1; - exno = 1; - - /* Fork a process and set the process group so that */ - /* it is different from this one. Fork 5 more children. */ - - pid1 = FORK_OR_VFORK(); - if (pid1 < 0) { - tst_brkm(TBROK, cleanup, "Fork of first child failed"); - } else if (pid1 == 0) { - setpgrp(); - for (i = 0; i < 5; i++) { - pid2 = FORK_OR_VFORK(); - if (pid2 < 0) { - tst_brkm(TBROK, cleanup, "Fork failed"); - } else if (pid2 == 0) { -#ifdef UCLINUX - if (self_exec(av[0], "") < 0) { - tst_brkm(TBROK, cleanup, - "self_exec of " - "child failed"); - } -#else - do_child(); -#endif - } - } - /* Kill all processes in this process group */ - TEST(kill(0, TEST_SIG)); - pause(); - exit(exno); - } else { - waitpid(pid1, &status, 0); - if (TEST_RETURN != 0) { - tst_brkm(TFAIL, cleanup, "%s failed - errno = " - "%d : %s", TCID, TEST_ERRNO, - strerror(TEST_ERRNO)); - } - } - - /* - * Check to see if the process was terminated with the - * expected signal. - */ - nsig = WTERMSIG(status); - if (nsig == TEST_SIG) { - tst_resm(TPASS, "received expected signal %d", - nsig); - } else { - tst_resm(TFAIL, - "expected signal %d received %d", - TEST_SIG, nsig); - } + /* Kill all processes in this process group */ + SAFE_KILL(0, TEST_SIG); + pause(); } - cleanup(); - tst_exit(); + /* + * Check to see if the process was terminated with the + * expected signal. + */ + SAFE_WAITPID(pid, &status, 0); + + nsig = WTERMSIG(status); + if (nsig == TEST_SIG) + tst_res(TPASS, "received expected signal %d", nsig); + else + tst_res(TFAIL, "expected signal %d received %d", TEST_SIG, nsig); } -/* - * do_child() - */ -void do_child(void) -{ - int exno = 1; - - pause(); - exit(exno); -} - -/* - * setup() - performs all ONE TIME setup for this test - */ -void setup(void) -{ - - TEST_PAUSE; -} - -/* - * cleanup() - performs all the ONE TIME cleanup for this test at completion - * or premature exit. - */ -void cleanup(void) -{ - -} +static struct tst_test test = { + .test_all = run, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/kill/kill09.c b/testcases/kernel/syscalls/kill/kill09.c deleted file mode 100755 index c5878923..00000000 --- a/testcases/kernel/syscalls/kill/kill09.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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: kill09.c,v 1.8 2009/08/28 13:22:51 vapier Exp $ */ -/********************************************************** - * - * OS Test - Silicon Graphics, Inc. - * - * TEST IDENTIFIER : kill09 - * - * EXECUTED BY : anyone - * - * TEST TITLE : Basic test for kill(2) - * - * PARENT DOCUMENT : usctpl01 - * - * TEST CASE TOTAL : 1 - * - * WALL CLOCK TIME : 1 - * - * CPU TYPES : ALL - * - * AUTHOR : William Roske - * - * CO-PILOT : Dave Fenner - * - * DATE STARTED : 03/30/92 - * - * INITIAL RELEASE : UNICOS 7.0 - * - * TEST CASES - * - * 1.) kill(2) returns...(See Description) - * - * INPUT SPECIFICATIONS - * The standard options for system call tests are accepted. - * (See the parse_opts(3) man page). - * - * OUTPUT SPECIFICATIONS - *$ - * DURATION - * Terminates - with frequency and infinite modes. - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * RESOURCES - * None - * - * ENVIRONMENTAL NEEDS - * No run-time environmental needs. - * - * SPECIAL PROCEDURAL REQUIREMENTS - * None - * - * INTERCASE DEPENDENCIES - * None - * - * DETAILED DESCRIPTION - * This is a Phase I test for the kill(2) system call. It is intended - * to provide a limited exposure of the system call, for now. It - * should/will be extended when full functional tests are written for - * kill(2). - * - * Setup: - * Setup signal handling. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, Issue a PASS message. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * - * - *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/ - -#include -#include -#include -#include -#include -#include - -#include "test.h" - -void setup(); -void cleanup(); -void alarm_handler(int sig); -void do_child(); - -char *TCID = "kill09"; -int TST_TOTAL = 1; - -int fork_pid; - -int main(int ac, char **av) -{ - int lc; - int status; - - tst_parse_opts(ac, av, NULL, NULL); - -#ifdef UCLINUX - maybe_run_child(&do_child, ""); -#endif - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - if ((fork_pid = FORK_OR_VFORK()) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); - - if (fork_pid == 0) { -#ifdef UCLINUX - if (self_exec(av[0], "") < 0) { - tst_brkm(TBROK, cleanup, - "self_exec of child failed"); - } -#else - do_child(); -#endif - } - - TEST(kill(fork_pid, SIGKILL)); - if (TEST_RETURN == -1) - tst_resm(TFAIL | TTERRNO, "kill(.., SIGKILL) failed"); - else { - tst_resm(TPASS, - "kill(%d, SIGKILL) returned %ld", - fork_pid, TEST_RETURN); - } - - waitpid(0, &status, WNOHANG); - - } - - cleanup(); - tst_exit(); -} - -void do_child(void) -{ - /* - * Setup alarm signal if we don't get the signal to prevent this process - * from hanging around forever. - */ - signal(SIGALRM, alarm_handler); - alarm(20); - pause(); - exit(1); -} - -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - (void)signal(SIGCHLD, SIG_IGN); - - TEST_PAUSE; - -} - -void cleanup(void) -{ -} - -void alarm_handler(int sig) -{ - exit(8); -} diff --git a/testcases/kernel/syscalls/kill/kill11.c b/testcases/kernel/syscalls/kill/kill11.c index 918f17f2..3cf62fea 100755 --- a/testcases/kernel/syscalls/kill/kill11.c +++ b/testcases/kernel/syscalls/kill/kill11.c @@ -6,15 +6,16 @@ * Copyright (c) International Business Machines Corp., 2002 * Copyright (c) Cyril Hrubis 2014 * - * Test checks that when a child is killed by its parent with sig, it - * returns the correct values(sig and core dump bit) to the waiting parent. - * * RESTRICTIONS * The ulimit for core file size must be greater than 0. */ +/*\ + * Test checks that when a child is killed by its parent with sig, it + * returns the correct values(sig and core dump bit) to the waiting parent. + */ + #define _GNU_SOURCE -#include #include #include #include diff --git a/testcases/kernel/syscalls/kill/kill12.c b/testcases/kernel/syscalls/kill/kill12.c index 8ab641d3..f864bdcb 100755 --- a/testcases/kernel/syscalls/kill/kill12.c +++ b/testcases/kernel/syscalls/kill/kill12.c @@ -95,7 +95,7 @@ int main(int argc, char **argv) fflush(temp); chflag = 0; - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid < 0) { forkfail(); } diff --git a/testcases/kernel/syscalls/kill/kill13.c b/testcases/kernel/syscalls/kill/kill13.c index 66ae37bc..ca281ca6 100755 --- a/testcases/kernel/syscalls/kill/kill13.c +++ b/testcases/kernel/syscalls/kill/kill13.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Reproducer of CVE-2018-10124; INT_MIN negation. * * On most two's complement CPUs negation of INT_MIN will result in diff --git a/testcases/kernel/syscalls/landlock/.gitignore b/testcases/kernel/syscalls/landlock/.gitignore new file mode 100644 index 00000000..8c88803d --- /dev/null +++ b/testcases/kernel/syscalls/landlock/.gitignore @@ -0,0 +1,11 @@ +landlock_exec +landlock01 +landlock02 +landlock03 +landlock04 +landlock05 +landlock06 +landlock07 +landlock08 +landlock09 +landlock10 diff --git a/testcases/kernel/syscalls/landlock/Makefile b/testcases/kernel/syscalls/landlock/Makefile new file mode 100644 index 00000000..3734d103 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +landlock07: LDLIBS += $(KEYUTILS_LIBS) + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/landlock/landlock01.c b/testcases/kernel/syscalls/landlock/landlock01.c new file mode 100644 index 00000000..bf206dae --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock01.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that landlock_create_ruleset syscall fails with the right + * error codes: + * + * - EINVAL Unknown flags, or unknown access, or too small size + * - E2BIG size is too big + * - EFAULT attr was not a valid address + * - ENOMSG Empty accesses (i.e., attr->handled_access_fs is 0) + */ + +#include "landlock_common.h" + +static struct tst_landlock_ruleset_attr_abi1 *ruleset_attr; +static struct tst_landlock_ruleset_attr_abi1 *null_attr; +static size_t rule_size; +static size_t rule_small_size; +static size_t rule_big_size; + +static struct tcase { + struct tst_landlock_ruleset_attr_abi1 **attr; + uint64_t access_fs; + size_t *size; + uint32_t flags; + int exp_errno; + char *msg; +} tcases[] = { + {&ruleset_attr, -1, &rule_size, 0, EINVAL, "Unknown access"}, + {&ruleset_attr, 0, &rule_small_size, 0, EINVAL, "Size is too small"}, + {&ruleset_attr, 0, &rule_size, -1, EINVAL, "Unknown flags"}, + {&ruleset_attr, 0, &rule_big_size, 0, E2BIG, "Size is too big"}, + {&null_attr, 0, &rule_size, 0, EFAULT, "Invalid attr address"}, + {&ruleset_attr, 0, &rule_size, 0, ENOMSG, "Empty accesses"}, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + if (*tc->attr) + (*tc->attr)->handled_access_fs = tc->access_fs; + + TST_EXP_FAIL(tst_syscall(__NR_landlock_create_ruleset, + *tc->attr, *tc->size, tc->flags), + tc->exp_errno, + "%s", + tc->msg); + + if (TST_RET >= 0) + SAFE_CLOSE(TST_RET); +} + +static void setup(void) +{ + verify_landlock_is_enabled(); + + rule_size = sizeof(struct tst_landlock_ruleset_attr_abi1); + rule_small_size = rule_size - 1; + + rule_big_size = SAFE_SYSCONF(_SC_PAGESIZE) + 1; +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .needs_root = 1, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi1)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock02.c b/testcases/kernel/syscalls/landlock/landlock02.c new file mode 100644 index 00000000..37dc72d3 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock02.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that landlock_add_rule syscall fails with the right + * error codes: + * + * - EINVAL flags is not 0, or the rule accesses are inconsistent + * - ENOMSG Empty accesses (i.e., rule_attr->allowed_access is 0) + * - EBADF ruleset_fd is not a file descriptor for the current thread, + * or a member of rule_attr is not a file descriptor as expected + * - EBADFD ruleset_fd is not a ruleset file descriptor, or a member of + * rule_attr is not the expected file descriptor type + * - EFAULT rule_attr was not a valid address + */ + +#include "landlock_common.h" + +static struct tst_landlock_ruleset_attr_abi1 *attr_abi1; +static struct tst_landlock_ruleset_attr_abi4 *attr_abi4; +static struct tst_landlock_ruleset_attr_abi6 *attr_abi6; +static struct landlock_path_beneath_attr *path_beneath_attr; +static struct landlock_path_beneath_attr *rule_null; +static struct landlock_net_port_attr *net_port_attr; +static int ruleset_fd; +static int invalid_fd = -1; +static int abi_current; + +static struct tcase { + int *fd; + int rule_type; + struct landlock_path_beneath_attr **path_attr; + struct landlock_net_port_attr **net_attr; + int access; + int parent_fd; + int net_port; + uint32_t flags; + int exp_errno; + int abi_ver; + char *msg; +} tcases[] = { + { + .fd = &ruleset_fd, + .path_attr = &path_beneath_attr, + .access = LANDLOCK_ACCESS_FS_EXECUTE, + .flags = 1, + .exp_errno = EINVAL, + .abi_ver = 1, + .msg = "Invalid flags" + }, + { + .fd = &ruleset_fd, + .path_attr = &path_beneath_attr, + .access = LANDLOCK_ACCESS_FS_EXECUTE, + .exp_errno = EINVAL, + .abi_ver = 1, + .msg = "Invalid rule type" + }, + { + .fd = &ruleset_fd, + .rule_type = LANDLOCK_RULE_PATH_BENEATH, + .path_attr = &path_beneath_attr, + .exp_errno = ENOMSG, + .abi_ver = 1, + .msg = "Empty accesses" + }, + { + .fd = &invalid_fd, + .path_attr = &path_beneath_attr, + .access = LANDLOCK_ACCESS_FS_EXECUTE, + .exp_errno = EBADF, + .abi_ver = 1, + .msg = "Invalid file descriptor" + }, + { + .fd = &ruleset_fd, + .rule_type = LANDLOCK_RULE_PATH_BENEATH, + .path_attr = &path_beneath_attr, + .access = LANDLOCK_ACCESS_FS_EXECUTE, + .parent_fd = -1, + .exp_errno = EBADF, + .abi_ver = 1, + .msg = "Invalid parent fd" + }, + { + .fd = &ruleset_fd, + .rule_type = LANDLOCK_RULE_PATH_BENEATH, + .path_attr = &rule_null, + .exp_errno = EFAULT, + .abi_ver = 1, + .msg = "Invalid rule attr" + }, + { + .fd = &ruleset_fd, + .rule_type = LANDLOCK_RULE_NET_PORT, + .net_attr = &net_port_attr, + .access = LANDLOCK_ACCESS_FS_EXECUTE, + .net_port = 448, + .exp_errno = EINVAL, + .abi_ver = 4, + .msg = "Invalid access rule for network type" + }, + { + .fd = &ruleset_fd, + .rule_type = LANDLOCK_RULE_NET_PORT, + .net_attr = &net_port_attr, + .access = LANDLOCK_ACCESS_NET_BIND_TCP, + .net_port = INT16_MAX + 1, + .exp_errno = EINVAL, + .abi_ver = 4, + .msg = "Socket port greater than 65535" + }, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + void *attr = NULL; + + if (tc->abi_ver > abi_current) { + tst_res(TCONF, "Minimum ABI required: %d", tc->abi_ver); + return; + } + + if (tc->path_attr && *tc->path_attr) { + (*tc->path_attr)->allowed_access = tc->access; + (*tc->path_attr)->parent_fd = tc->parent_fd; + + attr = *tc->path_attr; + } else if (tc->net_attr && *tc->net_attr) { + (*tc->net_attr)->allowed_access = tc->access; + (*tc->net_attr)->port = tc->net_port; + + attr = *tc->net_attr; + } + + TST_EXP_FAIL(tst_syscall(__NR_landlock_add_rule, + *tc->fd, tc->rule_type, attr, tc->flags), + tc->exp_errno, "%s", tc->msg); +} + +static void setup(void) +{ + abi_current = verify_landlock_is_enabled(); + + attr_abi1->handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE; + attr_abi4->handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE; + attr_abi6->handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE; + + if (abi_current < 4) { + ruleset_fd = TST_EXP_FD_SILENT(tst_syscall(__NR_landlock_create_ruleset, + attr_abi1, sizeof(struct tst_landlock_ruleset_attr_abi1), 0)); + } else if (abi_current < 6) { + ruleset_fd = TST_EXP_FD_SILENT(tst_syscall(__NR_landlock_create_ruleset, + attr_abi4, sizeof(struct tst_landlock_ruleset_attr_abi4), 0)); + } else { + ruleset_fd = TST_EXP_FD_SILENT(tst_syscall(__NR_landlock_create_ruleset, + attr_abi6, sizeof(struct tst_landlock_ruleset_attr_abi6), 0)); + } +} + +static void cleanup(void) +{ + if (ruleset_fd != -1) + SAFE_CLOSE(ruleset_fd); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .bufs = (struct tst_buffers []) { + {&attr_abi1, .size = sizeof(struct tst_landlock_ruleset_attr_abi1)}, + {&attr_abi4, .size = sizeof(struct tst_landlock_ruleset_attr_abi4)}, + {&attr_abi6, .size = sizeof(struct tst_landlock_ruleset_attr_abi6)}, + {&path_beneath_attr, .size = sizeof(struct landlock_path_beneath_attr)}, + {&net_port_attr, .size = sizeof(struct landlock_net_port_attr)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock03.c b/testcases/kernel/syscalls/landlock/landlock03.c new file mode 100644 index 00000000..3c0cd56b --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock03.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that landlock_restrict_self syscall fails with the right + * error codes: + * + * - EINVAL flags is not 0 + * - EBADF ruleset_fd is not a file descriptor for the current thread + * - EBADFD ruleset_fd is not a ruleset file descriptor + * - EPERM ruleset doesn't have CAP_SYS_ADMIN in its namespace + * - E2BIG The maximum number of stacked rulesets is reached for the current + * thread + */ + +#include "landlock_common.h" + +#define MAX_STACKED_RULESETS 16 + +static struct tst_landlock_ruleset_attr_abi1 *ruleset_attr; +static int ruleset_fd = -1; +static int ruleset_invalid = -1; +static int file_fd = -1; + +#define ID_NAME(x) .id = x, .name = #x +static struct tst_cap dropadmin = { + .action = TST_CAP_DROP, + ID_NAME(CAP_SYS_ADMIN), +}; + +static struct tst_cap needadmin = { + .action = TST_CAP_REQ, + ID_NAME(CAP_SYS_ADMIN), +}; + +static struct tcase { + int *fd; + uint32_t flags; + int exp_errno; + char *msg; +} tcases[] = { + {&ruleset_fd, -1, EINVAL, "Invalid flags"}, + {&ruleset_invalid, 0, EBADF, "Invalid file descriptor"}, + {&file_fd, 0, EBADFD, "Not a ruleset file descriptor"}, + {&ruleset_fd, 0, EPERM, "File descriptor doesn't have CAP_SYS_ADMIN"}, + {&ruleset_fd, 0, E2BIG, "Maximum number of stacked rulesets is reached"}, +}; + +static void run_child(struct tcase *tc) +{ + if (tc->exp_errno == EPERM) + tst_cap_action(&dropadmin); + + if (tc->exp_errno == E2BIG) { + for (int i = 0; i < MAX_STACKED_RULESETS; i++) { + TST_EXP_PASS_SILENT(tst_syscall(__NR_landlock_restrict_self, + *tc->fd, tc->flags)); + if (TST_RET == -1) + return; + } + } + + TST_EXP_FAIL(tst_syscall(__NR_landlock_restrict_self, *tc->fd, tc->flags), + tc->exp_errno, + "%s", tc->msg); + + if (tc->exp_errno == EPERM) + tst_cap_action(&needadmin); +} + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + if (!SAFE_FORK()) { + run_child(tc); + _exit(0); + } +} + +static void setup(void) +{ + verify_landlock_is_enabled(); + + ruleset_attr->handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE; + + ruleset_fd = TST_EXP_FD_SILENT(tst_syscall(__NR_landlock_create_ruleset, + ruleset_attr, sizeof(struct tst_landlock_ruleset_attr_abi1), 0)); + + file_fd = SAFE_OPEN("junk.bin", O_CREAT, 0777); +} + +static void cleanup(void) +{ + if (ruleset_fd != -1) + SAFE_CLOSE(ruleset_fd); + + if (file_fd != -1) + SAFE_CLOSE(file_fd); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .needs_root = 1, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi1)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock04.c b/testcases/kernel/syscalls/landlock/landlock04.c new file mode 100644 index 00000000..737fdbf2 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock04.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that all landlock filesystem rules are working properly. + * The way we do it is to verify that all disabled syscalls are not working but + * the one we enabled via specifc landlock rules. + */ + +#include "landlock_common.h" +#include "landlock_tester.h" +#include "tst_safe_stdio.h" + +static struct tst_landlock_ruleset_attr_abi1 *ruleset_attr; +static struct landlock_path_beneath_attr *path_beneath_attr; +static int ruleset_fd = -1; + +static struct tvariant { + int access; + char *desc; +} tvariants[] = { + { + LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_EXECUTE, + TST_TO_STR_(LANDLOCK_ACCESS_FS_EXECUTE) + }, + { + LANDLOCK_ACCESS_FS_WRITE_FILE, + TST_TO_STR_(LANDLOCK_ACCESS_FS_WRITE_FILE) + }, + { + LANDLOCK_ACCESS_FS_READ_FILE, + TST_TO_STR_(LANDLOCK_ACCESS_FS_READ_FILE) + }, + { + LANDLOCK_ACCESS_FS_READ_DIR, + TST_TO_STR_(LANDLOCK_ACCESS_FS_READ_DIR) + }, + { + LANDLOCK_ACCESS_FS_REMOVE_DIR, + TST_TO_STR_(LANDLOCK_ACCESS_FS_REMOVE_DIR) + }, + { + LANDLOCK_ACCESS_FS_REMOVE_FILE, + TST_TO_STR_(LANDLOCK_ACCESS_FS_REMOVE_FILE) + }, + { + LANDLOCK_ACCESS_FS_MAKE_CHAR, + TST_TO_STR_(LANDLOCK_ACCESS_FS_MAKE_CHAR) + }, + { + LANDLOCK_ACCESS_FS_MAKE_BLOCK, + TST_TO_STR_(LANDLOCK_ACCESS_FS_MAKE_BLOCK) + }, + { + LANDLOCK_ACCESS_FS_MAKE_REG, + TST_TO_STR_(LANDLOCK_ACCESS_FS_MAKE_REG) + }, + { + LANDLOCK_ACCESS_FS_MAKE_SOCK, + TST_TO_STR_(LANDLOCK_ACCESS_FS_MAKE_SOCK) + }, + { + LANDLOCK_ACCESS_FS_MAKE_FIFO, + TST_TO_STR_(LANDLOCK_ACCESS_FS_MAKE_FIFO) + }, + { + LANDLOCK_ACCESS_FS_MAKE_SYM, + TST_TO_STR_(LANDLOCK_ACCESS_FS_MAKE_SYM) + }, + { + LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, + TST_TO_STR_(LANDLOCK_ACCESS_FS_TRUNCATE) + }, +}; + +static void run(void) +{ + struct tvariant variant = tvariants[tst_variant]; + + tester_setup_files(); + + if (!SAFE_FORK()) { + enforce_ruleset(ruleset_fd); + tester_run_all_fs_rules(variant.access); + + _exit(0); + } + + tst_reap_children(); + tester_cleanup_files(); +} + +static void enable_exec_libs(const int ruleset_fd) +{ + FILE *fp; + char line[1024]; + char path[PATH_MAX]; + char dependency[8][PATH_MAX]; + int count = 0; + int duplicate = 0; + + fp = SAFE_FOPEN("/proc/self/maps", "r"); + + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, ".so") == NULL) + continue; + + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path); + + for (int i = 0; i < count; i++) { + if (strcmp(path, dependency[i]) == 0) { + duplicate = 1; + break; + } + } + + if (duplicate) { + duplicate = 0; + continue; + } + + strncpy(dependency[count], path, PATH_MAX); + count++; + + tst_res(TINFO, "Enable read/exec permissions for %s", path); + + path_beneath_attr->allowed_access = + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_EXECUTE; + path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC); + + SAFE_LANDLOCK_ADD_RULE( + ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, path_beneath_attr, 0); + + SAFE_CLOSE(path_beneath_attr->parent_fd); + } + + SAFE_FCLOSE(fp); +} + +static void setup(void) +{ + struct tvariant variant = tvariants[tst_variant]; + + verify_landlock_is_enabled(); + + tst_res(TINFO, "Testing %s", variant.desc); + + ruleset_attr->handled_access_fs = tester_get_all_fs_rules(); + + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET( + ruleset_attr, sizeof(struct tst_landlock_ruleset_attr_abi1), 0); + + /* since our binary is dynamically linked, we need to enable dependences + * to be read and executed + */ + enable_exec_libs(ruleset_fd); + + /* sandbox folder has to exist before creating the rule */ + if (access(SANDBOX_FOLDER, F_OK) == -1) + SAFE_MKDIR(SANDBOX_FOLDER, PERM_MODE); + + path_beneath_attr->allowed_access = variant.access; + path_beneath_attr->parent_fd = SAFE_OPEN( + SANDBOX_FOLDER, O_PATH | O_CLOEXEC); + + SAFE_LANDLOCK_ADD_RULE( + ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, path_beneath_attr, 0); + + SAFE_CLOSE(path_beneath_attr->parent_fd); +} + +static void cleanup(void) +{ + if (ruleset_fd != -1) + SAFE_CLOSE(ruleset_fd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .needs_root = 1, + .test_variants = ARRAY_SIZE(tvariants), + .resource_files = (const char *[]) { + TESTAPP, + NULL, + }, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi1)}, + {&path_beneath_attr, .size = sizeof(struct landlock_path_beneath_attr)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + TST_CAP(TST_CAP_REQ, CAP_MKNOD), + {} + }, + .mount_device = 1, + .mntpoint = SANDBOX_FOLDER, + .all_filesystems = 1, + .timeout = 360, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock05.c b/testcases/kernel/syscalls/landlock/landlock05.c new file mode 100644 index 00000000..ad5e932b --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock05.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies LANDLOCK_ACCESS_FS_REFER access in the + * landlock sandbox. + * + * [Algorithm] + * + * - apply LANDLOCK_ACCESS_FS_REFER in the folder1 + * - apply LANDLOCK_ACCESS_FS_REFER in the folder2 + * - create folder3 + * - verify that file can be moved from folder1 to folder2 + * - verify that file can't be moved from folder1 to folder3 + */ + +#include "landlock_common.h" + +#define MNTPOINT "sandbox" +#define DIR1 MNTPOINT"/folder1" +#define DIR2 MNTPOINT"/folder2" +#define DIR3 MNTPOINT"/folder3" +#define FILENAME1 DIR1"/file" +#define FILENAME2 DIR2"/file" +#define FILENAME3 DIR3"/file" + +static struct tst_landlock_ruleset_attr_abi1 *ruleset_attr; +static struct landlock_path_beneath_attr *path_beneath_attr; + +static void run(void) +{ + if (SAFE_FORK()) + return; + + TST_EXP_PASS(rename(FILENAME1, FILENAME2)); + if (TST_RET == -1) + return; + + TST_EXP_FAIL(rename(FILENAME2, FILENAME3), EXDEV); + TST_EXP_PASS(rename(FILENAME2, FILENAME1)); + + _exit(0); +} + +static void setup(void) +{ + int abi; + int ruleset_fd; + + abi = verify_landlock_is_enabled(); + if (abi < 2) + tst_brk(TCONF, "LANDLOCK_ACCESS_FS_REFER is unsupported on ABI < 2"); + + SAFE_MKDIR(DIR1, 0640); + SAFE_MKDIR(DIR2, 0640); + SAFE_MKDIR(DIR3, 0640); + SAFE_TOUCH(FILENAME1, 0640, NULL); + + tst_res(TINFO, "Applying LANDLOCK_ACCESS_FS_REFER"); + + ruleset_attr->handled_access_fs = + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET( + ruleset_attr, sizeof(struct tst_landlock_ruleset_attr_abi1), 0); + + apply_landlock_fs_rule( + path_beneath_attr, + ruleset_fd, + LANDLOCK_ACCESS_FS_REFER, + DIR1); + + apply_landlock_fs_rule( + path_beneath_attr, + ruleset_fd, + LANDLOCK_ACCESS_FS_REFER, + DIR2); + + enforce_ruleset(ruleset_fd); + + SAFE_CLOSE(ruleset_fd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .needs_root = 1, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi1)}, + {&path_beneath_attr, .size = sizeof(struct landlock_path_beneath_attr)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .skip_filesystems = (const char *[]) { + "vfat", + "exfat", + NULL + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock06.c b/testcases/kernel/syscalls/landlock/landlock06.c new file mode 100644 index 00000000..c2ac20d5 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock06.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies LANDLOCK_ACCESS_FS_IOCTL_DEV access in the + * landlock sandbox by creating a pipe and testing that ioctl() can be executed + * on it. The test is also verifying that some of the I/O operations can be + * always executed no matter the sandbox rules. + */ + +#include "landlock_common.h" +#include + +#define MNTPOINT "sandbox" +#define FILENAME MNTPOINT"/fifo" + +static struct tst_landlock_ruleset_attr_abi1 *ruleset_attr; +static struct landlock_path_beneath_attr *path_beneath_attr; +static int file_fd = -1; +static int dev_fd = -1; + +static void run(void) +{ + if (SAFE_FORK()) + return; + + int flag = 0; + size_t sz = 0; + + TST_EXP_PASS(ioctl(file_fd, FIONREAD, &sz)); + TST_EXP_PASS(ioctl(dev_fd, FIOCLEX)); + TST_EXP_PASS(ioctl(dev_fd, FIONCLEX)); + TST_EXP_PASS(ioctl(dev_fd, FIONBIO, &flag)); + TST_EXP_PASS(ioctl(dev_fd, FIOASYNC, &flag)); + + _exit(0); +} + +static void setup(void) +{ + if (verify_landlock_is_enabled() < 5) + tst_brk(TCONF, "LANDLOCK_ACCESS_FS_IOCTL_DEV is not supported"); + + SAFE_TOUCH(FILENAME, 0640, NULL); + + file_fd = SAFE_OPEN(FILENAME, O_RDONLY | O_NONBLOCK, 0640); + dev_fd = SAFE_OPEN("/dev/zero", O_RDONLY | O_NONBLOCK, 0640); + + tst_res(TINFO, "Applying LANDLOCK_ACCESS_FS_IOCTL_DEV"); + + ruleset_attr->handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV; + + apply_landlock_fs_layer( + ruleset_attr, + sizeof(struct tst_landlock_ruleset_attr_abi1), + path_beneath_attr, + MNTPOINT, + LANDLOCK_ACCESS_FS_IOCTL_DEV + ); +} + +static void cleanup(void) +{ + if (dev_fd != -1) + SAFE_CLOSE(dev_fd); + + if (file_fd != -1) + SAFE_CLOSE(file_fd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi1)}, + {&path_beneath_attr, .size = sizeof(struct landlock_path_beneath_attr)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .skip_filesystems = (const char *[]) { + "vfat", + NULL + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock07.c b/testcases/kernel/syscalls/landlock/landlock07.c new file mode 100644 index 00000000..7ef0f756 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock07.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/** + * CVE-2024-42318 + * + * Test to check if system is affected by Landlock Houdini bug: + * https://www.suse.com/security/cve/CVE-2024-42318.html + * + * Kernel bug fixed in: + * + * commit 39705a6c29f8a2b93cf5b99528a55366c50014d1 + * Author: Jann Horn + * Date: Wed Jul 24 14:49:01 2024 +0200 + * + * landlock: Don't lose track of restrictions on cred_transfer + */ + +#include "tst_test.h" +#include "lapi/keyctl.h" +#include "lapi/prctl.h" +#include "landlock_common.h" + +static struct tst_landlock_ruleset_attr_abi1 *ruleset_attr; +static int ruleset_fd; + +static pid_t spawn_houdini(void) +{ + pid_t pid; + + SAFE_KEYCTL(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0); + + pid = SAFE_FORK(); + if (!pid) { + SAFE_KEYCTL(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0); + SAFE_KEYCTL(KEYCTL_SESSION_TO_PARENT, 0, 0, 0, 0); + exit(0); + } + + return pid; +} + +static void run(void) +{ + pid_t pid_houdini; + + if (SAFE_FORK()) + return; + + SAFE_PRCTL(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + SAFE_LANDLOCK_RESTRICT_SELF(ruleset_fd, 0); + + TST_EXP_FAIL(open("/dev/null", O_WRONLY), EACCES); + if (TST_RET != -1) { + SAFE_CLOSE(TST_RET); + return; + } + + pid_houdini = spawn_houdini(); + SAFE_WAITPID(pid_houdini, NULL, 0); + + TST_EXP_FAIL(open("/dev/null", O_WRONLY), EACCES); + if (TST_RET != -1) + SAFE_CLOSE(TST_RET); + + exit(0); +} + +static void setup(void) +{ + verify_landlock_is_enabled(); + + ruleset_attr->handled_access_fs = LANDLOCK_ACCESS_FS_WRITE_FILE; + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET( + ruleset_attr, + sizeof(struct tst_landlock_ruleset_attr_abi1), + 0); +} + +static void cleanup(void) +{ + if (ruleset_fd != -1) + SAFE_CLOSE(ruleset_fd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi1)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, + .tags = (const struct tst_tag[]) { + {"linux-git", "39705a6c29f8"}, + {"CVE", "2024-42318"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/landlock/landlock08.c b/testcases/kernel/syscalls/landlock/landlock08.c new file mode 100644 index 00000000..7c498e6d --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock08.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify the landlock support for bind()/connect() syscalls in IPV4 and IPV6 + * protocols. In particular, check that bind() is assigning the address only on + * the TCP port enforced by LANDLOCK_ACCESS_NET_BIND_TCP and check that + * connect() is connecting only to a specific TCP port enforced by + * LANDLOCK_ACCESS_NET_CONNECT_TCP. + * + * [Algorithm] + * + * Repeat the following procedure for IPV4 and IPV6: + * + * - create a socket on PORT1, bind() it and check if it passes + * - enforce the current sandbox with LANDLOCK_ACCESS_NET_BIND_TCP on PORT1 + * - create a socket on PORT1, bind() it and check if it passes + * - create a socket on PORT2, bind() it and check if it fails + * + * - create a server listening on PORT1 + * - create a socket on PORT1, connect() to it and check if it passes + * - enforce the current sandbox with LANDLOCK_ACCESS_NET_CONNECT_TCP on PORT1 + * - create a socket on PORT1, connect() to it and check if it passes + * - create a socket on PORT2, connect() to it and check if it fails + */ + +#include "landlock_common.h" + +static int variants[] = { + AF_INET, + AF_INET6, +}; + +static struct tst_landlock_ruleset_attr_abi4 *ruleset_attr; +static struct landlock_net_port_attr *net_port_attr; +static in_port_t *server_port; +static int addr_port; + +static void create_server(const int addr_family) +{ + struct socket_data socket; + struct sockaddr *addr = NULL; + + create_socket(&socket, addr_family, 0); + getsocket_addr(&socket, addr_family, &addr); + + SAFE_BIND(socket.fd, addr, socket.address_size); + SAFE_LISTEN(socket.fd, 1); + + *server_port = getsocket_port(&socket, addr_family); + + tst_res(TDEBUG, "Server listening on port %u", *server_port); + + TST_CHECKPOINT_WAKE_AND_WAIT(0); + + SAFE_CLOSE(socket.fd); +} + +static void test_bind(const int addr_family, const in_port_t port, + const int exp_err) +{ + struct socket_data socket; + struct sockaddr *addr = NULL; + + create_socket(&socket, addr_family, port); + getsocket_addr(&socket, addr_family, &addr); + + if (exp_err) { + TST_EXP_FAIL( + bind(socket.fd, addr, socket.address_size), + exp_err, "bind() access on port %u", port); + } else { + TST_EXP_PASS( + bind(socket.fd, addr, socket.address_size), + "bind() access on port %u", port); + } + + SAFE_CLOSE(socket.fd); +} + +static void test_connect(const int addr_family, const in_port_t port, + const int exp_err) +{ + struct socket_data socket; + struct sockaddr *addr = NULL; + + create_socket(&socket, addr_family, port); + getsocket_addr(&socket, addr_family, &addr); + + if (exp_err) { + TST_EXP_FAIL( + connect(socket.fd, addr, socket.address_size), + exp_err, "connect() on port %u", port); + } else { + TST_EXP_PASS( + connect(socket.fd, addr, socket.address_size), + "connect() on port %u", port); + } + + SAFE_CLOSE(socket.fd); +} + +static void run(void) +{ + int addr_family = variants[tst_variant]; + + tst_res(TINFO, "Using %s protocol", + addr_family == AF_INET ? "IPV4" : "IPV6"); + + if (!SAFE_FORK()) { + create_server(addr_family); + exit(0); + } + + TST_CHECKPOINT_WAIT(0); + + /* verify bind() syscall accessibility */ + if (!SAFE_FORK()) { + ruleset_attr->handled_access_net = + LANDLOCK_ACCESS_NET_BIND_TCP; + + test_bind(addr_family, addr_port, 0); + + tst_res(TINFO, "Enable bind() access only for port %u", + addr_port); + + apply_landlock_net_layer( + ruleset_attr, + sizeof(struct tst_landlock_ruleset_attr_abi4), + net_port_attr, + addr_port, + LANDLOCK_ACCESS_NET_BIND_TCP); + + test_bind(addr_family, addr_port, 0); + test_bind(addr_family, addr_port + 0x80, EACCES); + + exit(0); + } + + /* verify connect() syscall accessibility */ + if (!SAFE_FORK()) { + ruleset_attr->handled_access_net = + LANDLOCK_ACCESS_NET_CONNECT_TCP; + + test_connect(addr_family, *server_port, 0); + + tst_res(TINFO, "Enable connect() access only on port %u", + *server_port); + + apply_landlock_net_layer( + ruleset_attr, + sizeof(struct tst_landlock_ruleset_attr_abi4), + net_port_attr, + *server_port, + LANDLOCK_ACCESS_NET_CONNECT_TCP); + + test_connect(addr_family, *server_port, 0); + test_connect(addr_family, *server_port + 0x80, EACCES); + + TST_CHECKPOINT_WAKE(0); + + exit(0); + } +} + +static void setup(void) +{ + if (verify_landlock_is_enabled() < 4) + tst_brk(TCONF, "Landlock network is not supported"); + + addr_port = TST_GET_UNUSED_PORT(AF_INET, SOCK_STREAM); + + server_port = SAFE_MMAP(NULL, sizeof(in_port_t), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); +} + +static void cleanup(void) +{ + if (server_port) + SAFE_MUNMAP(server_port, sizeof(in_port_t)); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .needs_checkpoints = 1, + .forks_child = 1, + .test_variants = ARRAY_SIZE(variants), + .bufs = (struct tst_buffers[]) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi4)}, + {&net_port_attr, .size = sizeof(struct landlock_net_port_attr)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + TST_CAP(TST_CAP_REQ, CAP_NET_BIND_SERVICE), + {} + }, + .needs_kconfigs = (const char *[]) { + "CONFIG_INET=y", + NULL + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock09.c b/testcases/kernel/syscalls/landlock/landlock09.c new file mode 100644 index 00000000..2e7f0021 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock09.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that landlock's LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET rule reject any + * connect() coming from a client on a different server domain, but accept any + * connection. + */ + +#include +#include "tst_test.h" +#include "landlock_common.h" + +#define SOCKET_NAME "test.sock" +#define ABSTRACT_SOCKET_NAME "\0"SOCKET_NAME +#define SOCKET_LENGTH (offsetof(struct sockaddr_un, sun_path) + strlen(SOCKET_NAME) + 1) + +enum { + DOMAIN_CLIENT = 0, + DOMAIN_SERVER, + DOMAIN_BOTH, +}; + +static struct tst_landlock_ruleset_attr_abi6 *ruleset_attr; + +static void scoped_sandbox(const char *from) +{ + tst_res(TINFO, "Enforcing rule LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET on %s", from); + + ruleset_attr->scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET; + apply_landlock_scoped_layer(ruleset_attr, sizeof(*ruleset_attr)); +} + +static void run_client(void) +{ + if (tst_variant == DOMAIN_CLIENT) + scoped_sandbox("client"); + + int sendsock; + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + .sun_path = ABSTRACT_SOCKET_NAME, + }; + + TST_CHECKPOINT_WAIT(0); + + tst_res(TINFO, "Connecting to UNIX socket"); + + sendsock = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); + + if (tst_variant != DOMAIN_CLIENT) + TST_EXP_PASS(connect(sendsock, (struct sockaddr *)&addr, SOCKET_LENGTH)); + else + TST_EXP_FAIL(connect(sendsock, (struct sockaddr *)&addr, SOCKET_LENGTH), EPERM); + + SAFE_CLOSE(sendsock); + + TST_CHECKPOINT_WAKE(0); +} + +static void run_server(void) +{ + if (tst_variant == DOMAIN_SERVER) + scoped_sandbox("server"); + + int recvsock; + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + .sun_path = ABSTRACT_SOCKET_NAME, + }; + + recvsock = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0); + + SAFE_BIND(recvsock, (struct sockaddr *)&addr, SOCKET_LENGTH); + SAFE_LISTEN(recvsock, 5); + + tst_res(TINFO, "Listening on UNIX socket"); + + TST_CHECKPOINT_WAKE_AND_WAIT(0); + + SAFE_CLOSE(recvsock); +} + +static void run(void) +{ + /* isolate test inside a process so we won't stack too many + * layers (-E2BIG) when there are multiple test's iterations + */ + if (SAFE_FORK()) + return; + + if (tst_variant == DOMAIN_BOTH) + scoped_sandbox("server and client"); + + if (!SAFE_FORK()) { + run_client(); + exit(0); + } + + run_server(); + + tst_reap_children(); +} + +static void setup(void) +{ + int abi; + + abi = verify_landlock_is_enabled(); + if (abi < 6) + tst_brk(TCONF, "LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET is unsupported on ABI < 6"); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .needs_root = 1, + .forks_child = 1, + .needs_checkpoints = 1, + .test_variants = 3, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi6)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock10.c b/testcases/kernel/syscalls/landlock/landlock10.c new file mode 100644 index 00000000..a29e3bca --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock10.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that landlock's LANDLOCK_SCOPE_SIGNAL rule rejects any signal coming + * from a process on a different domain, but accept signals from processes in + * the same domain. + */ + +#include "tst_test.h" +#include "landlock_common.h" + +static struct tst_landlock_ruleset_attr_abi6 *ruleset_attr; + +enum { + DOMAIN_PAUSED = 0, + DOMAIN_KILLER, + DOMAIN_BOTH, +}; + +static void scoped_sandbox(const char *from) +{ + tst_res(TINFO, "Enforcing rule LANDLOCK_SCOPE_SIGNAL for %s process", from); + + ruleset_attr->scoped = LANDLOCK_SCOPE_SIGNAL; + apply_landlock_scoped_layer(ruleset_attr, sizeof(*ruleset_attr)); +} + +static void run(void) +{ + /* isolate test inside a process so we won't stack too many + * layers (-E2BIG) when there are multiple test's iterations + */ + if (SAFE_FORK()) + return; + + if (tst_variant == DOMAIN_BOTH) + scoped_sandbox("paused and killer"); + + pid_t paused_pid; + pid_t killer_pid; + + paused_pid = SAFE_FORK(); + if (!paused_pid) { + if (tst_variant == DOMAIN_PAUSED) + scoped_sandbox("paused"); + + TST_CHECKPOINT_WAKE(0); + pause(); + exit(0); + } + + TST_CHECKPOINT_WAIT(0); + TST_PROCESS_STATE_WAIT(paused_pid, 'S', 10000); + + killer_pid = SAFE_FORK(); + if (!killer_pid) { + if (tst_variant == DOMAIN_KILLER) + scoped_sandbox("killer"); + + TST_CHECKPOINT_WAKE(0); + + if (tst_variant == DOMAIN_KILLER) + TST_EXP_FAIL(kill(paused_pid, SIGKILL), EPERM); + else + TST_EXP_PASS(kill(paused_pid, SIGKILL)); + + exit(0); + } + + TST_CHECKPOINT_WAIT(0); + SAFE_WAITPID(killer_pid, NULL, 0); + + if (kill(paused_pid, SIGKILL) == -1) { + if (errno != ESRCH) + tst_brk(TBROK | TERRNO, "kill(%u, SIGKILL) error", paused_pid); + } + + SAFE_WAITPID(paused_pid, NULL, 0); +} + +static void setup(void) +{ + int abi; + + abi = verify_landlock_is_enabled(); + if (abi < 6) + tst_brk(TCONF, "LANDLOCK_SCOPE_SIGNAL is unsupported on ABI < 6"); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .needs_root = 1, + .forks_child = 1, + .needs_checkpoints = 1, + .test_variants = 3, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi6)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + {} + }, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock_common.h b/testcases/kernel/syscalls/landlock/landlock_common.h new file mode 100644 index 00000000..8857745d --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock_common.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef LANDLOCK_COMMON_H__ +#define LANDLOCK_COMMON_H__ + +#include "tst_test.h" +#include "lapi/prctl.h" +#include "lapi/fcntl.h" +#include "lapi/landlock.h" + +#define IPV4_LOCALHOST "127.0.0.1" +#define IPV6_LOCALHOST "::1" + +struct socket_data { + struct sockaddr_in addr_ipv4; + struct sockaddr_in6 addr_ipv6; + size_t address_size; + int fd; +}; + +static inline int verify_landlock_is_enabled(void) +{ + int abi; + + abi = tst_syscall(__NR_landlock_create_ruleset, + NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + + if (abi < 0) { + if (errno == EOPNOTSUPP) { + tst_brk(TCONF, "Landlock is currently disabled. " + "Please enable it either via CONFIG_LSM or " + "'lsm' kernel parameter."); + } + + tst_brk(TBROK | TERRNO, "landlock_create_ruleset error"); + } + + tst_res(TINFO, "Landlock ABI v%d", abi); + + return abi; +} + +static inline void apply_landlock_fs_rule( + struct landlock_path_beneath_attr *path_beneath_attr, + const int ruleset_fd, + const int access, + const char *path) +{ + path_beneath_attr->allowed_access = access; + path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC); + + SAFE_LANDLOCK_ADD_RULE( + ruleset_fd, + LANDLOCK_RULE_PATH_BENEATH, + path_beneath_attr, + 0); + + SAFE_CLOSE(path_beneath_attr->parent_fd); +} + +static inline void apply_landlock_net_rule( + struct landlock_net_port_attr *net_attr, + const int ruleset_fd, + const uint64_t port, + const uint64_t access) +{ + net_attr->port = port; + net_attr->allowed_access = access; + + SAFE_LANDLOCK_ADD_RULE( + ruleset_fd, + LANDLOCK_RULE_NET_PORT, + net_attr, + 0); +} + +static inline void enforce_ruleset(const int ruleset_fd) +{ + SAFE_PRCTL(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + SAFE_LANDLOCK_RESTRICT_SELF(ruleset_fd, 0); +} + +static inline void apply_landlock_fs_layer( + void *ruleset_attr, size_t attr_size, + struct landlock_path_beneath_attr *path_beneath_attr, + const char *path, + const int access) +{ + int ruleset_fd; + + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(ruleset_attr, attr_size, 0); + + apply_landlock_fs_rule(path_beneath_attr, ruleset_fd, access, path); + enforce_ruleset(ruleset_fd); + + SAFE_CLOSE(ruleset_fd); +} + +static inline void apply_landlock_net_layer( + void *ruleset_attr, size_t attr_size, + struct landlock_net_port_attr *net_port_attr, + const in_port_t port, + const uint64_t access) +{ + int ruleset_fd; + + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(ruleset_attr, attr_size, 0); + + apply_landlock_net_rule(net_port_attr, ruleset_fd, port, access); + enforce_ruleset(ruleset_fd); + + SAFE_CLOSE(ruleset_fd); +} + +static inline void apply_landlock_scoped_layer( + void *ruleset_attr, size_t attr_size) +{ + int ruleset_fd; + + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(ruleset_attr, attr_size, 0); + enforce_ruleset(ruleset_fd); + + SAFE_CLOSE(ruleset_fd); +} + +static inline in_port_t getsocket_port(struct socket_data *socket, + const int addr_family) +{ + struct sockaddr_in addr_ipv4; + struct sockaddr_in6 addr_ipv6; + socklen_t len; + in_port_t port = 0; + + switch (addr_family) { + case AF_INET: + len = sizeof(addr_ipv4); + memset(&addr_ipv4, 0, len); + + SAFE_GETSOCKNAME(socket->fd, (struct sockaddr *)&addr_ipv4, &len); + port = ntohs(addr_ipv4.sin_port); + break; + case AF_INET6: + len = sizeof(addr_ipv6); + memset(&addr_ipv6, 0, len); + + SAFE_GETSOCKNAME(socket->fd, (struct sockaddr *)&addr_ipv6, &len); + port = ntohs(addr_ipv6.sin6_port); + break; + default: + tst_brk(TBROK, "Unsupported protocol"); + break; + }; + + return port; +} + +static inline void create_socket(struct socket_data *socket, + const int addr_family, const in_port_t port) +{ + memset(socket, 0, sizeof(struct socket_data)); + + switch (addr_family) { + case AF_INET: + if (!port) { + tst_init_sockaddr_inet_bin(&socket->addr_ipv4, + INADDR_ANY, 0); + } else { + tst_init_sockaddr_inet(&socket->addr_ipv4, + IPV4_LOCALHOST, port); + } + + socket->address_size = sizeof(struct sockaddr_in); + break; + case AF_INET6: + if (!port) { + tst_init_sockaddr_inet6_bin(&socket->addr_ipv6, + &in6addr_any, 0); + } else { + tst_init_sockaddr_inet6(&socket->addr_ipv6, + IPV6_LOCALHOST, port); + } + + socket->address_size = sizeof(struct sockaddr_in6); + break; + default: + tst_brk(TBROK, "Unsupported protocol"); + return; + }; + + socket->fd = SAFE_SOCKET(addr_family, SOCK_STREAM | SOCK_CLOEXEC, 0); +} + +static inline void getsocket_addr(struct socket_data *socket, + const int addr_family, struct sockaddr **addr) +{ + switch (addr_family) { + case AF_INET: + *addr = (struct sockaddr *)&socket->addr_ipv4; + break; + case AF_INET6: + *addr = (struct sockaddr *)&socket->addr_ipv6; + break; + default: + break; + }; +} +#endif /* LANDLOCK_COMMON_H__ */ diff --git a/testcases/kernel/syscalls/landlock/landlock_exec.c b/testcases/kernel/syscalls/landlock/landlock_exec.c new file mode 100644 index 00000000..aae5c76b --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock_exec.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +int main(void) +{ + return 0; +} diff --git a/testcases/kernel/syscalls/landlock/landlock_tester.h b/testcases/kernel/syscalls/landlock/landlock_tester.h new file mode 100644 index 00000000..dcebc45c --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock_tester.h @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef LANDLOCK_TESTER_H__ + +#include "tst_test.h" +#include "lapi/landlock.h" +#include + +#define PERM_MODE 0700 + +#define SANDBOX_FOLDER "sandbox" +#define TESTAPP "landlock_exec" + +#define FILE_EXEC SANDBOX_FOLDER"/"TESTAPP +#define FILE_READ SANDBOX_FOLDER"/file_read" +#define FILE_WRITE SANDBOX_FOLDER"/file_write" +#define FILE_REMOVE SANDBOX_FOLDER"/file_remove" +#define FILE_UNLINK SANDBOX_FOLDER"/file_unlink" +#define FILE_UNLINKAT SANDBOX_FOLDER"/file_unlinkat" +#define FILE_TRUNCATE SANDBOX_FOLDER"/file_truncate" +#define FILE_REGULAR SANDBOX_FOLDER"/regular0" +#define FILE_SOCKET SANDBOX_FOLDER"/socket0" +#define FILE_FIFO SANDBOX_FOLDER"/fifo0" +#define FILE_SYM0 SANDBOX_FOLDER"/symbolic0" +#define FILE_SYM1 SANDBOX_FOLDER"/symbolic1" +#define DIR_READDIR SANDBOX_FOLDER"/dir_readdir" +#define DIR_RMDIR SANDBOX_FOLDER"/dir_rmdir" +#define DEV_CHAR0 SANDBOX_FOLDER"/chardev0" +#define DEV_BLK0 SANDBOX_FOLDER"/blkdev0" + +#define ALL_RULES (\ + LANDLOCK_ACCESS_FS_EXECUTE | \ + LANDLOCK_ACCESS_FS_WRITE_FILE | \ + LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_READ_DIR | \ + LANDLOCK_ACCESS_FS_REMOVE_DIR | \ + LANDLOCK_ACCESS_FS_REMOVE_FILE | \ + LANDLOCK_ACCESS_FS_MAKE_CHAR | \ + LANDLOCK_ACCESS_FS_MAKE_DIR | \ + LANDLOCK_ACCESS_FS_MAKE_REG | \ + LANDLOCK_ACCESS_FS_MAKE_SOCK | \ + LANDLOCK_ACCESS_FS_MAKE_FIFO | \ + LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ + LANDLOCK_ACCESS_FS_MAKE_SYM | \ + LANDLOCK_ACCESS_FS_REFER | \ + LANDLOCK_ACCESS_FS_TRUNCATE | \ + LANDLOCK_ACCESS_FS_IOCTL_DEV) + +static char *readdir_files[] = { + DIR_READDIR"/file0", + DIR_READDIR"/file1", + DIR_READDIR"/file2", +}; + +static int dev_chr; +static int dev_blk; + +static int tester_get_all_fs_rules(void) +{ + int abi; + int all_rules = ALL_RULES; + + abi = SAFE_LANDLOCK_CREATE_RULESET( + NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + + if (abi < 2) + all_rules &= ~LANDLOCK_ACCESS_FS_REFER; + + if (abi < 3) + all_rules &= ~LANDLOCK_ACCESS_FS_TRUNCATE; + + if (abi < 5) + all_rules &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; + + return all_rules; +} + +/* This function setup the sandbox folder before running the test. + * Run it __before__ enforcing the sandbox rules and ensure that SANDBOX_FOLDER + * has been created already. + */ +static void tester_setup_files(void) +{ + /* folders */ + SAFE_MKDIR(DIR_RMDIR, PERM_MODE); + SAFE_MKDIR(DIR_READDIR, PERM_MODE); + for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++) + SAFE_TOUCH(readdir_files[i], PERM_MODE, NULL); + + /* files */ + tst_fill_file(FILE_READ, 'a', getpagesize(), 1); + SAFE_TOUCH(FILE_WRITE, PERM_MODE, NULL); + SAFE_TOUCH(FILE_REMOVE, PERM_MODE, NULL); + SAFE_TOUCH(FILE_UNLINK, PERM_MODE, NULL); + SAFE_TOUCH(FILE_UNLINKAT, PERM_MODE, NULL); + SAFE_TOUCH(FILE_TRUNCATE, PERM_MODE, NULL); + SAFE_TOUCH(FILE_SYM0, PERM_MODE, NULL); + SAFE_CP(TESTAPP, FILE_EXEC); + + /* devices */ + dev_chr = makedev(1, 3); + dev_blk = makedev(7, 0); +} + +static void _remove_file(const char *path) +{ + if (access(path, F_OK) != -1) + SAFE_UNLINK(path); +} + +/* This function cleanup the sandbox folder after running the tests. + * Run it after getting out from the sandbox. + */ +static void tester_cleanup_files(void) +{ + if (access(DIR_RMDIR, F_OK) != -1) + SAFE_RMDIR(DIR_RMDIR); + + for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++) + _remove_file(readdir_files[i]); + + if (access(DIR_READDIR, F_OK) != -1) + SAFE_RMDIR(DIR_READDIR); + + struct stat st; + + if (lstat(FILE_SYM1, &st) != -1) + SAFE_UNLINK(FILE_SYM1); + + _remove_file(FILE_READ); + _remove_file(FILE_WRITE); + _remove_file(FILE_REMOVE); + _remove_file(FILE_UNLINK); + _remove_file(FILE_UNLINKAT); + _remove_file(FILE_TRUNCATE); + _remove_file(FILE_SYM0); + _remove_file(FILE_EXEC); + + _remove_file(DEV_BLK0); + _remove_file(DEV_CHAR0); + _remove_file(FILE_FIFO); + _remove_file(FILE_SOCKET); + _remove_file(FILE_REGULAR); +} + +static void _test_exec(const int result) +{ + int status; + pid_t pid; + char *const args[] = {(char *)FILE_EXEC, NULL}; + + tst_res(TINFO, "Test binary execution"); + + pid = SAFE_FORK(); + if (!pid) { + int rval; + + if (result == TPASS) { + rval = execve(FILE_EXEC, args, NULL); + if (rval == -1) + tst_res(TFAIL | TERRNO, "Failed to execute test binary"); + } else { + TST_EXP_FAIL(execve(FILE_EXEC, args, NULL), EACCES); + } + + _exit(1); + } + + SAFE_WAITPID(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return; + + tst_res(result, "Test binary has been executed"); +} + +static void _test_write(const int result) +{ + tst_res(TINFO, "Test writing file"); + + if (result == TPASS) + TST_EXP_FD(open(FILE_WRITE, O_WRONLY, PERM_MODE)); + else + TST_EXP_FAIL(open(FILE_WRITE, O_WRONLY, PERM_MODE), EACCES); + + if (TST_RET != -1) + SAFE_CLOSE(TST_RET); +} + +static void _test_read(const int result) +{ + tst_res(TINFO, "Test reading file"); + + if (result == TPASS) + TST_EXP_FD(open(FILE_READ, O_RDONLY, PERM_MODE)); + else + TST_EXP_FAIL(open(FILE_READ, O_RDONLY, PERM_MODE), EACCES); + + if (TST_RET != -1) + SAFE_CLOSE(TST_RET); +} + +static void _test_readdir(const int result) +{ + tst_res(TINFO, "Test reading directory"); + + DIR *dir; + struct dirent *de; + int files_counted = 0; + + dir = opendir(DIR_READDIR); + if (!dir) { + tst_res(result == TPASS ? TFAIL : TPASS, + "Can't read '%s' directory", DIR_READDIR); + + return; + } + + tst_res(result, "Can read '%s' directory", DIR_READDIR); + if (result == TFAIL) + return; + + while ((de = readdir(dir)) != NULL) { + if (de->d_type != DT_REG) + continue; + + for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++) { + if (readdir_files[i] == NULL) + continue; + + if (strstr(readdir_files[i], de->d_name) != NULL) + files_counted++; + } + } + + SAFE_CLOSEDIR(dir); + + TST_EXP_EQ_LI(files_counted, ARRAY_SIZE(readdir_files)); +} + +static void _test_rmdir(const int result) +{ + tst_res(TINFO, "Test removing directory"); + + if (result == TPASS) + TST_EXP_PASS(rmdir(DIR_RMDIR)); + else + TST_EXP_FAIL(rmdir(DIR_RMDIR), EACCES); +} + +static void _test_rmfile(const int result) +{ + tst_res(TINFO, "Test removing file"); + + if (result == TPASS) { + TST_EXP_PASS(unlink(FILE_UNLINK)); + TST_EXP_PASS(remove(FILE_REMOVE)); + } else { + TST_EXP_FAIL(unlink(FILE_UNLINK), EACCES); + TST_EXP_FAIL(remove(FILE_REMOVE), EACCES); + } +} + +static void _test_make( + const char *path, + const int type, + const int dev, + const int result) +{ + tst_res(TINFO, "Test normal or special files creation"); + + if (result == TPASS) + TST_EXP_PASS(mknod(path, type | 0400, dev)); + else + TST_EXP_FAIL(mknod(path, type | 0400, dev), EACCES); +} + +static void _test_symbolic(const int result) +{ + tst_res(TINFO, "Test symbolic links"); + + if (result == TPASS) + TST_EXP_PASS(symlink(FILE_SYM0, FILE_SYM1)); + else + TST_EXP_FAIL(symlink(FILE_SYM0, FILE_SYM1), EACCES); +} + +static void _test_truncate(const int result) +{ + int fd; + + tst_res(TINFO, "Test truncating file"); + + if (result == TPASS) { + TST_EXP_PASS(truncate(FILE_TRUNCATE, 10)); + + fd = SAFE_OPEN(FILE_TRUNCATE, O_WRONLY, PERM_MODE); + if (fd != -1) { + TST_EXP_PASS(ftruncate(fd, 10)); + SAFE_CLOSE(fd); + } + + fd = TST_EXP_FD(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC, PERM_MODE)); + if (fd != -1) + SAFE_CLOSE(fd); + } else { + TST_EXP_FAIL(truncate(FILE_TRUNCATE, 10), EACCES); + + fd = open(FILE_TRUNCATE, O_WRONLY, PERM_MODE); + if (fd != -1) { + TST_EXP_FAIL(ftruncate(fd, 10), EACCES); + SAFE_CLOSE(fd); + } + + TST_EXP_FAIL(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC, PERM_MODE), + EACCES); + + if (TST_RET != -1) + SAFE_CLOSE(TST_RET); + } +} + +static void tester_run_fs_rules(const int rules, const int result) +{ + if (rules & LANDLOCK_ACCESS_FS_EXECUTE) + _test_exec(result); + + if (rules & LANDLOCK_ACCESS_FS_WRITE_FILE) + _test_write(result); + + if (rules & LANDLOCK_ACCESS_FS_READ_FILE) + _test_read(result); + + if (rules & LANDLOCK_ACCESS_FS_READ_DIR) + _test_readdir(result); + + if (rules & LANDLOCK_ACCESS_FS_REMOVE_DIR) + _test_rmdir(result); + + if (rules & LANDLOCK_ACCESS_FS_REMOVE_FILE) + _test_rmfile(result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_REG) + _test_make(FILE_REGULAR, S_IFREG, 0, result); + + if (strcmp(tst_device->fs_type, "vfat") && + strcmp(tst_device->fs_type, "exfat")) { + if (rules & LANDLOCK_ACCESS_FS_MAKE_CHAR) + _test_make(DEV_CHAR0, S_IFCHR, dev_chr, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_BLOCK) + _test_make(DEV_BLK0, S_IFBLK, dev_blk, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_SOCK) + _test_make(FILE_SOCKET, S_IFSOCK, 0, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_FIFO) + _test_make(FILE_FIFO, S_IFIFO, 0, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_SYM) + _test_symbolic(result); + } + + if (rules & LANDLOCK_ACCESS_FS_TRUNCATE) { + int abi; + + abi = SAFE_LANDLOCK_CREATE_RULESET( + NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + + if (abi < 3) { + tst_res(TINFO, "Skip truncate test. Minimum ABI version is 3"); + return; + } + + _test_truncate(result); + } +} + +static inline void tester_run_all_fs_rules(const int pass_rules) +{ + int fail_rules; + int all_rules; + + all_rules = tester_get_all_fs_rules(); + fail_rules = all_rules & ~pass_rules; + + tester_run_fs_rules(pass_rules, TPASS); + tester_run_fs_rules(fail_rules, TFAIL); +} + +#endif diff --git a/testcases/kernel/syscalls/lchown/.gitignore b/testcases/kernel/syscalls/lchown/.gitignore index e3c7bc29..491ffa08 100755 --- a/testcases/kernel/syscalls/lchown/.gitignore +++ b/testcases/kernel/syscalls/lchown/.gitignore @@ -2,5 +2,3 @@ /lchown01_16 /lchown02 /lchown02_16 -/lchown03 -/lchown03_16 diff --git a/testcases/kernel/syscalls/lchown/Makefile b/testcases/kernel/syscalls/lchown/Makefile index 305fee28..1a66088e 100755 --- a/testcases/kernel/syscalls/lchown/Makefile +++ b/testcases/kernel/syscalls/lchown/Makefile @@ -5,8 +5,6 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk -SRCS := $(sort $(wildcard $(abs_srcdir)/lchown*.c)) - include $(abs_srcdir)/../utils/compat_16.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/lchown/lchown01.c b/testcases/kernel/syscalls/lchown/lchown01.c index 4e6076e1..6204c2ac 100755 --- a/testcases/kernel/syscalls/lchown/lchown01.c +++ b/testcases/kernel/syscalls/lchown/lchown01.c @@ -1,170 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Description: - * Verify that, lchown(2) succeeds to change the owner and group of a file - * specified by path to any numeric owner(uid)/group(gid) values when invoked - * by super-user. - * - * Expected Result: - * lchown(2) should return 0 and the ownership set on the file should match - * the numeric values contained in owner and group respectively. - * - * HISTORY * 07/2001 Ported by Wayne Boyer * 11/2010 Code cleanup by Cyril Hrubis chrubis@suse.cz + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -#include -#include -#include -#include -#include -#include -#include +/*\ + * Verify that, lchown(2) succeeds to change the owner and group of a file + * specified by path to any numeric owner(uid)/group(gid) values when invoked + * by super-user. + * + * lchown(2) should return 0 and the ownership set on the file should match + * the numeric values contained in owner and group respectively. + */ -#include "test.h" -#include "safe_macros.h" -#include "compat_16.h" +#include "tst_test.h" -#define FILE_MODE (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) -#define TESTFILE "testfile" -#define SFILE "slink_file" +#define TESTFILE "testfile" +#define SFILE "slink_file" -TCID_DEFINE(lchown01); -int TST_TOTAL = 5; - -struct test_case_t { +static struct test_case_t { char *desc; uid_t user_id; gid_t group_id; +} test_cases[] = { + { "Change Owner/Group ids", 700, 701 }, + { "Change Owner id only", 702, -1 }, + { "Change Owner/Group ids", 703, 701 }, + { "Change Group id only", -1, 704 }, + { "Change Group/Group ids", 703, 705 }, + { "Change none", -1, -1 }, }; -static struct test_case_t test_cases[] = { - {"Change Owner/Group ids", 700, 701}, - {"Change Owner id only", 702, -1}, - {"Change Owner/Group ids", 703, 701}, - {"Change Group id only", -1, 704}, - {"Change Group/Group ids", 703, 705}, - {"Change none", -1, -1}, - {NULL, 0, 0} -}; - -static void setup(void); -static void cleanup(void); - -int main(int argc, char *argv[]) +static void run(unsigned int i) { struct stat stat_buf; - int lc; - int i; + struct test_case_t *tc = &test_cases[i]; + uid_t user_id = tc->user_id; + gid_t group_id = tc->group_id; - tst_parse_opts(argc, argv, NULL, NULL); + SAFE_LSTAT(SFILE, &stat_buf); + uid_t cmp_usr_id = user_id == -1 ? stat_buf.st_uid : user_id; + gid_t cmp_grp_id = group_id == -1 ? stat_buf.st_gid : group_id; - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - for (i = 0; test_cases[i].desc != NULL; i++) { - uid_t user_id = test_cases[i].user_id; - gid_t group_id = test_cases[i].group_id; - char *test_desc = test_cases[i].desc; - - /* - * Call lchown(2) with different user id and - * group id (numeric values) to set it on - * symlink of testfile. - */ - TEST(LCHOWN(cleanup, SFILE, user_id, group_id)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL, - "lchown() Fails to %s, errno %d", - test_desc, TEST_ERRNO); - continue; - } - - if (lstat(SFILE, &stat_buf) < 0) { - tst_brkm(TFAIL, cleanup, "lstat(2) " - "%s failed, errno %d", - SFILE, TEST_ERRNO); - } - - if ((int)user_id == -1) { - if (i > 0) - user_id = - test_cases[i - 1].user_id; - else - user_id = geteuid(); - } - - if ((int)group_id == -1) { - if (i > 0) - group_id = - test_cases[i - 1].group_id; - else - group_id = getegid(); - } - - /* - * Check for expected Ownership ids - * set on testfile. - */ - if ((stat_buf.st_uid != user_id) || - (stat_buf.st_gid != group_id)) { - tst_resm(TFAIL, - "%s: incorrect ownership set, " - "Expected %d %d", SFILE, - user_id, group_id); - } else { - tst_resm(TPASS, "lchown() succeeds to " - "%s of %s", test_desc, SFILE); - } - } - } - - cleanup(); - tst_exit(); + tst_res(TINFO, "%s", tc->desc); + SAFE_LCHOWN(SFILE, user_id, group_id); + SAFE_LSTAT(SFILE, &stat_buf); + TST_EXP_EQ_LI(stat_buf.st_uid, cmp_usr_id); + TST_EXP_EQ_LI(stat_buf.st_gid, cmp_grp_id); } static void setup(void) { - int fd; - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - tst_require_root(); - - TEST_PAUSE; - tst_tmpdir(); - - if ((fd = open(TESTFILE, O_RDWR | O_CREAT, FILE_MODE)) == -1) { - tst_brkm(TBROK, cleanup, "open failed"); - } - SAFE_CLOSE(cleanup, fd); - - SAFE_SYMLINK(cleanup, TESTFILE, SFILE); + SAFE_TOUCH(TESTFILE, 0644, NULL); + SAFE_SYMLINK(TESTFILE, SFILE); } -static void cleanup(void) -{ - tst_rmdir(); -} +static struct tst_test test = { + .tcnt = ARRAY_SIZE(test_cases), + .test = run, + .setup = setup, + .needs_tmpdir = 1, + .needs_root = 1, +}; diff --git a/testcases/kernel/syscalls/lchown/lchown02.c b/testcases/kernel/syscalls/lchown/lchown02.c index 97966f6b..335e48ea 100755 --- a/testcases/kernel/syscalls/lchown/lchown02.c +++ b/testcases/kernel/syscalls/lchown/lchown02.c @@ -1,291 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: lchown02 - * - * Test Description: - * Verify that, - * 1) lchown(2) returns -1 and sets errno to EPERM if the effective user id - * of process does not match the owner of the file and the process is - * not super user. - * 2) lchown(2) returns -1 and sets errno to EACCES if search permission is - * denied on a component of the path prefix. - * 3) lchown(2) returns -1 and sets errno to EFAULT if pathname points - * outside user's accessible address space. - * 4) lchown(2) returns -1 and sets errno to ENAMETOOLONG if the pathname - * component is too long. - * 5) lchown(2) returns -1 and sets errno to ENOTDIR if the directory - * component in pathname is not a directory. - * 6) lchown(2) returns -1 and sets errno to ENOENT if the specified file - * does not exists. - * - * Expected Result: - * lchown() should fail with return value -1 and set expected errno. - * - * HISTORY * 07/2001 Ported by Wayne Boyer * 11/2010 Rewritten by Cyril Hrubis chrubis@suse.cz + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that lchown(2) fails with errno: + * + * - EPERM, if the effective user id of process does not match the owner of + * the file and the process is not super user. + * - EACCES, if search permission is denied on a component of the path prefix. + * - EFAULT, if pathname points outside user's accessible address space. + * - ENAMETOOLONG, if the pathname component is too long. + * - ENOTDIR, if the directory component in pathname is not a directory. + * - ENOENT, if the specified file does not exists. + * - ELOOP, if too many symbolic links were encountered in resolving path. + * - EROFS, if the file is on a read-only file system. */ -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" -#include "compat_16.h" +#include "tst_test.h" +#include "compat_tst_16.h" -#define TEST_USER "nobody" -#define MODE_RWX S_IRWXU | S_IRWXG | S_IRWXO -#define FILE_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH -#define DIR_TEMP "testdir_1" -#define TEST_FILE1 "tfile_1" -#define SFILE1 "sfile_1" -#define TEST_FILE2 "testdir_1/tfile_2" -#define SFILE2 "testdir_1/sfile_2" -#define TFILE3 "t_file" -#define SFILE3 "t_file/sfile" - -TCID_DEFINE(lchown02); -int TST_TOTAL = 7; - -static void setup(void); -static void cleanup(void); -static void setup_eperm(int pos); -static void setup_eacces(int pos); -static void setup_enotdir(int pos); -static void setup_longpath(int pos); -static void setup_efault(int pos); - -static char path[PATH_MAX + 2]; - -struct test_case_t { - char *pathname; - char *desc; - int exp_errno; - void (*setup) (int pos); -}; - -static struct test_case_t test_cases[] = { - {SFILE1, "Process is not owner/root", EPERM, setup_eperm}, - {SFILE2, "Search permission denied", EACCES, setup_eacces}, - {NULL, "Unaccessible address space", EFAULT, setup_efault}, - {path, "Pathname too long", ENAMETOOLONG, setup_longpath}, - {SFILE3, "Path contains regular file", ENOTDIR, setup_enotdir}, - {"", "Pathname is empty", ENOENT, NULL}, - {NULL, NULL, 0, NULL} -}; +#define TEST_USER "nobody" +#define DIR_TEMP "testdir_1" +#define TFILE1 "tfile_1" +#define SFILE1 "sfile_1" +#define TFILE2 "testdir_1/tfile_2" +#define SFILE2 "testdir_1/sfile_2" +#define TFILE3 "t_file" +#define SFILE3 "t_file/sfile" +#define MAXPATH (PATH_MAX + 2) +#define EFILE1 "infinite_loop1/eloop" +#define TEST_EROFS "mntpoint" +static char *sfile1; +static char *sfile2; +static char *bad_addr; +static char *maxpath; +static char *sfile3; +static char *empty; +static char *eloop; +static char *erofs; static struct passwd *ltpuser; -int main(int argc, char *argv[]) +static struct test_case_t { + char **pathname; + char *desc; + int exp_errno; +} test_cases[] = { + { &sfile1, "Process is not owner/root", EPERM }, + { &sfile2, "Search permission denied", EACCES }, + { &bad_addr, "Unaccessible address space", EFAULT }, + { &maxpath, "Pathname too long", ENAMETOOLONG }, + { &sfile3, "Path contains regular file", ENOTDIR }, + { &empty, "Pathname is empty", ENOENT }, + { &eloop, "Too many symlinks", ELOOP }, + { &erofs, "Read-only filesystem", EROFS }, +}; + +static void run(unsigned int i) { - int lc; - uid_t user_id; - gid_t group_id; - int i; + struct test_case_t *tc = &test_cases[i]; - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - user_id = geteuid(); - UID16_CHECK(user_id, lchown, cleanup); - group_id = getegid(); - GID16_CHECK(group_id, lchown, cleanup); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; test_cases[i].desc != NULL; i++) { - char *file_name = test_cases[i].pathname; - char *test_desc = test_cases[i].desc; - - /* - * Call lchown(2) to test different test conditions. - * verify that it fails with -1 return value and - * sets appropriate errno. - */ - TEST(LCHOWN(cleanup, file_name, user_id, group_id)); - - /* Check return code from lchown(2) */ - if (TEST_RETURN == -1) { - if (TEST_ERRNO == test_cases[i].exp_errno) { - tst_resm(TPASS, - "lchown(2) fails, %s, errno:%d", - test_desc, TEST_ERRNO); - } else { - tst_resm(TFAIL, "lchown(2) fails, %s, " - "errno:%d, expected errno:%d", - test_desc, TEST_ERRNO, - test_cases[i].exp_errno); - } - } else { - tst_resm(TFAIL, "lchown(2) returned %ld, " - "expected -1, errno:%d", TEST_RETURN, - test_cases[i].exp_errno); - } - } - } - - cleanup(); - tst_exit(); + TST_EXP_FAIL(lchown(*tc->pathname, ltpuser->pw_uid, ltpuser->pw_gid), + tc->exp_errno, "%s", tc->desc); } static void setup(void) { - int i; + bad_addr = tst_get_bad_addr(NULL); - tst_sig(FORK, DEF_HANDLER, cleanup); + memset(maxpath, 'a', MAXPATH - 1); + maxpath[MAXPATH - 1] = 0; - tst_require_root(); + ltpuser = SAFE_GETPWNAM(TEST_USER); + SAFE_SETGID(ltpuser->pw_uid); - TEST_PAUSE; + UID16_CHECK(ltpuser->pw_uid, "lchown"); + GID16_CHECK(ltpuser->pw_gid, "lchown"); - /* change euid and gid to nobody */ - ltpuser = getpwnam(TEST_USER); + SAFE_TOUCH(TFILE1, 0666, NULL); + SAFE_SETEUID(0); + SAFE_SYMLINK(TFILE1, SFILE1); + SAFE_SETEUID(ltpuser->pw_uid); - if (ltpuser == NULL) - tst_brkm(TBROK, cleanup, "getpwnam failed"); + SAFE_SYMLINK("infinite_loop1", "infinite_loop2"); + SAFE_SYMLINK("infinite_loop2", "infinite_loop1"); - if (setgid(ltpuser->pw_uid) == -1) - tst_resm(TBROK | TERRNO, "setgid failed"); + SAFE_MKDIR(DIR_TEMP, 0777); + SAFE_TOUCH(TFILE2, 0666, NULL); + SAFE_SYMLINK(TFILE2, SFILE2); + SAFE_CHMOD(DIR_TEMP, 0644); - tst_tmpdir(); - - for (i = 0; test_cases[i].desc != NULL; i++) - if (test_cases[i].setup != NULL) - test_cases[i].setup(i); + SAFE_TOUCH(TFILE3, 0777, NULL); } -/* - * setup_eperm() - setup function for a test condition for which lchown(2) - * returns -1 and sets errno to EPERM. - * - * Create test file and symlink with uid 0. - */ -static void setup_eperm(int pos LTP_ATTRIBUTE_UNUSED) -{ - int fd; - - /* create a testfile */ - if ((fd = open(TEST_FILE1, O_RDWR | O_CREAT, 0666)) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "open failed"); - - SAFE_CLOSE(cleanup, fd); - - /* become root once more */ - if (seteuid(0) == -1) - tst_resm(TBROK | TERRNO, "setuid(0) failed"); - - /* create symling to testfile */ - SAFE_SYMLINK(cleanup, TEST_FILE1, SFILE1); - - /* back to the user nobody */ - if (seteuid(ltpuser->pw_uid) == -1) - tst_resm(TBROK | TERRNO, "seteuid(%d) failed", ltpuser->pw_uid); -} - -/* - * setup_eaccess() - setup function for a test condition for which lchown(2) - * returns -1 and sets errno to EACCES. - * - * Create a test directory under temporary directory and create a test file - * under this directory with mode "0666" permissions. - * Modify the mode permissions on test directory such that process will not - * have search permissions on test directory. - */ -static void setup_eacces(int pos LTP_ATTRIBUTE_UNUSED) -{ - int fd; - - /* create a test directory */ - SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX); - - /* create a file under test directory */ - if ((fd = open(TEST_FILE2, O_RDWR | O_CREAT, 0666)) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "open failed"); - - SAFE_CLOSE(cleanup, fd); - - /* create a symlink of testfile */ - SAFE_SYMLINK(cleanup, TEST_FILE2, SFILE2); - - /* modify mode permissions on test directory */ - SAFE_CHMOD(cleanup, DIR_TEMP, FILE_MODE); -} - -/* - * setup_efault() -- setup for a test condition where lchown(2) returns -1 and - * sets errno to EFAULT. - * - * Create "bad address" by explicitly mmaping anonymous page that may not be - * accesed (see PROT_NONE). - */ -static void setup_efault(int pos) -{ - test_cases[pos].pathname = tst_get_bad_addr(cleanup); -} - -/* - * setup_enotdir() - setup function for a test condition for which chown(2) - * returns -1 and sets errno to ENOTDIR. - * - * Create a regular file "t_file" to call lchown(2) on "t_file/sfile" later. - */ -static void setup_enotdir(int pos LTP_ATTRIBUTE_UNUSED) -{ - int fd; - - /* create a testfile under temporary directory */ - if ((fd = open(TFILE3, O_RDWR | O_CREAT, MODE_RWX)) == -1) { - tst_brkm(TBROK | TERRNO, cleanup, "open(2) %s failed", TFILE3); - } - - SAFE_CLOSE(cleanup, fd); -} - -/* - * longpath_setup() - setup to create a node with a name length exceeding - * the length of PATH_MAX. - */ -static void setup_longpath(int pos) -{ - memset(test_cases[pos].pathname, 'a', PATH_MAX + 1); - test_cases[pos].pathname[PATH_MAX + 1] = '\0'; -} - -static void cleanup(void) -{ - if (seteuid(0) == -1) { - tst_resm(TINFO | TERRNO, - "seteuid(2) failed to set the effective uid to 0"); - } - - tst_rmdir(); -} +static struct tst_test test = { + .tcnt = ARRAY_SIZE(test_cases), + .test = run, + .setup = setup, + .needs_root = 1, + .needs_tmpdir = 1, + .mntpoint = TEST_EROFS, + .needs_rofs = 1, + .bufs = (struct tst_buffers []) { + {&maxpath, .size = MAXPATH}, + {&sfile1, .str = SFILE1}, + {&sfile2, .str = SFILE2}, + {&sfile3, .str = SFILE3}, + {&eloop, .str = EFILE1}, + {&empty, .str = ""}, + { &erofs, .str = TEST_EROFS }, + {} + }, +}; diff --git a/testcases/kernel/syscalls/lchown/lchown03.c b/testcases/kernel/syscalls/lchown/lchown03.c deleted file mode 100755 index c26f54c2..00000000 --- a/testcases/kernel/syscalls/lchown/lchown03.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2014 Fujitsu Ltd. - * Author: Zeng Linggang - * - * 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. - */ -/* - * Test Description: - * Verify that, - * 1. lchown() fails with -1 return value and sets errno to ELOOP - * if too many symbolic links were encountered in resolving path. - * 2. lchown() fails with -1 return value and sets errno to EROFS - * if the file is on a read-only file system. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" -#include "compat_16.h" - -#define DIR_MODE (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP| \ - S_IXGRP|S_IROTH|S_IXOTH) -#define TEST_EROFS "mntpoint" - -static char test_eloop[PATH_MAX] = "."; -static const char *device; -static int mount_flag; - -static struct test_case_t { - char *pathname; - int exp_errno; -} test_cases[] = { - {test_eloop, ELOOP}, - {TEST_EROFS, EROFS}, -}; - -TCID_DEFINE(lchown03); -int TST_TOTAL = ARRAY_SIZE(test_cases); - -static void setup(void); -static void lchown_verify(const struct test_case_t *); -static void cleanup(void); - -int main(int argc, char *argv[]) -{ - int lc; - int i; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - for (i = 0; i < TST_TOTAL; i++) - lchown_verify(&test_cases[i]); - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - int i; - const char *fs_type; - - tst_require_root(); - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - fs_type = tst_dev_fs_type(); - device = tst_acquire_device(cleanup); - - if (!device) - tst_brkm(TCONF, cleanup, "Failed to acquire device"); - - SAFE_MKDIR(cleanup, "test_eloop", DIR_MODE); - SAFE_SYMLINK(cleanup, "../test_eloop", "test_eloop/test_eloop"); - for (i = 0; i < 43; i++) - strcat(test_eloop, "/test_eloop"); - - tst_mkfs(cleanup, device, fs_type, NULL, NULL); - SAFE_MKDIR(cleanup, TEST_EROFS, DIR_MODE); - SAFE_MOUNT(cleanup, device, TEST_EROFS, fs_type, MS_RDONLY, NULL); - mount_flag = 1; -} - -static void lchown_verify(const struct test_case_t *test) -{ - UID16_CHECK(geteuid(), "lchown", cleanup) - GID16_CHECK(getegid(), "lchown", cleanup) - - TEST(LCHOWN(cleanup, test->pathname, geteuid(), getegid())); - - if (TEST_RETURN != -1) { - tst_resm(TFAIL, "lchown() returned %ld, expected -1, errno=%d", - TEST_RETURN, test->exp_errno); - return; - } - - if (TEST_ERRNO == test->exp_errno) { - tst_resm(TPASS | TTERRNO, "lchown() failed as expected"); - } else { - tst_resm(TFAIL | TTERRNO, - "lchown() failed unexpectedly; expected: %d - %s", - test->exp_errno, - strerror(test->exp_errno)); - } -} - -static void cleanup(void) -{ - if (mount_flag && tst_umount(TEST_EROFS) < 0) - tst_resm(TWARN | TERRNO, "umount device:%s failed", device); - - if (device) - tst_release_device(device); - - tst_rmdir(); -} diff --git a/testcases/kernel/syscalls/lgetxattr/lgetxattr01.c b/testcases/kernel/syscalls/lgetxattr/lgetxattr01.c index 5c92d231..f98d11d8 100755 --- a/testcases/kernel/syscalls/lgetxattr/lgetxattr01.c +++ b/testcases/kernel/syscalls/lgetxattr/lgetxattr01.c @@ -1,18 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 Fujitsu Ltd. -* Author: Jinbao Huang -*/ + * Copyright (c) 2016 Fujitsu Ltd. + * Author: Jinbao Huang + */ -/* -* Test Name: lgetxattr01 -* -* Description: -* The testcase checks the basic functionality of the lgetxattr(2). -* In the case of a symbolic link, we only get the value of the -* extended attribute related to the link itself by name. -* -*/ +/*\ + * Check the basic functionality of the lgetxattr(2). + * + * In the case of a symbolic link, lgetxattr(2) only gets the value of the + * extended attribute related to the link itself by name. + */ #include "config.h" #include diff --git a/testcases/kernel/syscalls/lgetxattr/lgetxattr02.c b/testcases/kernel/syscalls/lgetxattr/lgetxattr02.c index 183239a2..459c1bf7 100755 --- a/testcases/kernel/syscalls/lgetxattr/lgetxattr02.c +++ b/testcases/kernel/syscalls/lgetxattr/lgetxattr02.c @@ -1,23 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 Fujitsu Ltd. -* Author: Jinbao Huang -*/ + * Copyright (c) 2016 Fujitsu Ltd. + * Author: Jinbao Huang + */ -/* -* Test Name: lgetxattr02 -* -* Description: -* 1) lgetxattr(2) fails if the named attribute does not exist. -* 2) lgetxattr(2) fails if the size of the value buffer is too small -* to hold the result. -* 3) lgetxattr(2) fails when attemptes to read from a invalid address. -* -* Expected Result: -* 1) lgetxattr(2) should return -1 and set errno to ENODATA. -* 2) lgetxattr(2) should return -1 and set errno to ERANGE. -* 3) lgetxattr(2) should return -1 and set errno to EFAULT. -*/ +/*\ + * Verify that, lgetxattr(2) returns -1 and sets errno to + * + * 1. ENODATA if the named attribute does not exist. + * 2. ERANGE if the size of the value buffer is too small to hold the result. + * 3. EFAULT when reading from an invalid address. + */ #include "config.h" #include diff --git a/testcases/kernel/syscalls/link/.gitignore b/testcases/kernel/syscalls/link/.gitignore index e5d7f1bb..b2b9db63 100755 --- a/testcases/kernel/syscalls/link/.gitignore +++ b/testcases/kernel/syscalls/link/.gitignore @@ -1,5 +1,4 @@ /link02 -/link03 /link04 /link05 /link08 diff --git a/testcases/kernel/syscalls/link/link02.c b/testcases/kernel/syscalls/link/link02.c index 3d265d6e..248e2c5b 100755 --- a/testcases/kernel/syscalls/link/link02.c +++ b/testcases/kernel/syscalls/link/link02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Tests that link(2) succeeds. */ diff --git a/testcases/kernel/syscalls/link/link03.c b/testcases/kernel/syscalls/link/link03.c deleted file mode 100755 index 1f45240a..00000000 --- a/testcases/kernel/syscalls/link/link03.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. - * AUTHOR : Richard Logan - * CO-PILOT : William Roske - * Copyright (c) 2014 Cyril Hrubis - * - * 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/ - * - */ - - /* - * Tests that link(2) succeds with creating n links. - */ - -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" - -static void setup(void); -static void help(void); -static void cleanup(void); - -char *TCID = "link03"; -int TST_TOTAL = 2; - -#define BASENAME "lkfile" - -static char fname[255]; -static int nlinks = 0; -static char *links_arg; - -option_t options[] = { - {"N:", NULL, &links_arg}, - {NULL, NULL, NULL} -}; - -int main(int ac, char **av) -{ - int lc; - struct stat buf; - int i, links; - char lname[255]; - - tst_parse_opts(ac, av, options, &help); - - if (links_arg) { - nlinks = atoi(links_arg); - - if (nlinks == 0) { - tst_brkm(TBROK, NULL, - "nlinks is not a positive number"); - } - - if (nlinks > 1000) { - tst_resm(TINFO, - "nlinks > 1000 - may get errno:%d (EMLINK)", - EMLINK); - } - } - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - if (nlinks) - links = nlinks; - else - links = (lc % 90) + 10; - - /* Create links - 1 hardlinks so that the st_nlink == links */ - for (i = 1; i < links; i++) { - sprintf(lname, "%s%d", fname, i); - TEST(link(fname, lname)); - - if (TEST_RETURN == -1) { - tst_brkm(TFAIL | TTERRNO, cleanup, - "link(%s, %s) Failed", fname, lname); - } - } - - SAFE_STAT(cleanup, fname, &buf); - - if (buf.st_nlink != (nlink_t)links) { - tst_resm(TFAIL, "Wrong number of links for " - "'%s' have %i, should be %i", - fname, (int)buf.st_nlink, links); - goto unlink; - } - - for (i = 1; i < links; i++) { - sprintf(lname, "%s%d", fname, i); - SAFE_STAT(cleanup, lname, &buf); - if (buf.st_nlink != (nlink_t)links) { - tst_resm(TFAIL, - "Wrong number of links for " - "'%s' have %i, should be %i", - lname, (int)buf.st_nlink, links); - goto unlink; - } - } - - tst_resm(TPASS, "link() passed and linkcounts=%d match", links); - -unlink: - for (i = 1; i < links; i++) { - sprintf(lname, "%s%d", fname, i); - SAFE_UNLINK(cleanup, lname); - } - } - - cleanup(); - tst_exit(); -} - -static void help(void) -{ - printf(" -N #links : create #links hard links every iteration\n"); -} - -static void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - sprintf(fname, "%s_%d", BASENAME, getpid()); - SAFE_TOUCH(cleanup, fname, 0700, NULL); -} - -static void cleanup(void) -{ - tst_rmdir(); -} diff --git a/testcases/kernel/syscalls/link/link04.c b/testcases/kernel/syscalls/link/link04.c index 9940ac66..1647785b 100755 --- a/testcases/kernel/syscalls/link/link04.c +++ b/testcases/kernel/syscalls/link/link04.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Negative test cases for link(2). * * This test program should contain test cases where link will fail regardless diff --git a/testcases/kernel/syscalls/link/link05.c b/testcases/kernel/syscalls/link/link05.c index 95787ec2..f3f28237 100755 --- a/testcases/kernel/syscalls/link/link05.c +++ b/testcases/kernel/syscalls/link/link05.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. - * AUTHOR : Richard Logan - * CO-PILOT : William Roske + * Authors: Richard Logan, William Roske * Copyright (c) 2014 Cyril Hrubis + * Copyright (c) Linux Test Project, 2001-2023 */ -/* - * Test if link(2) fails with EMLINK. +/*\ + * Tests that link(2) succeeds with creating 1000 links. */ #include @@ -15,25 +15,27 @@ #include "tst_test.h" #define BASENAME "lkfile" +#define NLINKS 1000 static char fname[255]; -static int nlinks = 1000; - static void verify_link(void) { - int cnt; + int cnt = 0, created; char lname[1024]; struct stat fbuf, lbuf; - for (cnt = 1; cnt < nlinks; cnt++) { - sprintf(lname, "%s_%d", fname, cnt); - TST_EXP_PASS_SILENT(link(fname, lname), "link(%s, %s)", fname, lname); + for (created = 1; created < NLINKS; created++) { + sprintf(lname, "%s_%d", fname, created); + TST_EXP_PASS_SILENT(link(fname, lname), "%d: link(%s, %s)", created, + fname, lname); + if (!TST_PASS) + goto cleanup; } SAFE_STAT(fname, &fbuf); - for (cnt = 1; cnt < nlinks; cnt++) { + for (cnt = 1; cnt < NLINKS; cnt++) { sprintf(lname, "%s_%d", fname, cnt); SAFE_STAT(lname, &lbuf); @@ -41,24 +43,21 @@ static void verify_link(void) (fbuf.st_nlink != lbuf.st_nlink)) { tst_res(TFAIL, - "link(%s, %s[1-%d]) ret %ld for %d " - "files, stat values do not match %d %d", - fname, fname, nlinks, - TST_RET, nlinks, + "link(%s, %s[1-%d]) ret %ld for %d files, stat values do not match %d %d", + fname, fname, NLINKS, TST_RET, NLINKS, (int)fbuf.st_nlink, (int)lbuf.st_nlink); break; } } - if (cnt >= nlinks) { + if (cnt == NLINKS) { tst_res(TPASS, - "link(%s, %s[1-%d]) ret %ld for %d files, " - "stat linkcounts match %d", - fname, fname, nlinks, TST_RET, - nlinks, (int)fbuf.st_nlink); + "link(%s, %s[1-%d]) ret %ld for %d files, stat linkcounts match %d", + fname, fname, NLINKS, TST_RET, NLINKS, (int)fbuf.st_nlink); } - for (cnt = 1; cnt < nlinks; cnt++) { +cleanup: + for (cnt = 1; cnt < created; cnt++) { sprintf(lname, "%s_%d", fname, cnt); SAFE_UNLINK(lname); } @@ -71,7 +70,7 @@ static void setup(void) } static struct tst_test test = { - .test_all = verify_link, - .setup = setup, - .needs_tmpdir = 1, + .test_all = verify_link, + .setup = setup, + .needs_tmpdir = 1, }; diff --git a/testcases/kernel/syscalls/link/link08.c b/testcases/kernel/syscalls/link/link08.c index d3e33d07..713001a3 100755 --- a/testcases/kernel/syscalls/link/link08.c +++ b/testcases/kernel/syscalls/link/link08.c @@ -1,23 +1,24 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014 Fujitsu Ltd. + * Copyright (c) Linux Test Project, 2014-2023 * Author: Zeng Linggang */ -/* - * Test Description: - * Verify that, - * 1. link() fails with -1 return value and sets errno to EPERM - * if oldpath is a directory. - * 2. link() fails with -1 return value and sets errno to EXDEV - * if oldpath and newpath are not on the same mounted file system( Linux - * permits a file system to be mounted at multiple points, but link() - * does not work across different mount points, even if the same - * file system is mounted on both. ). - * 3. link() fails with -1 return value and sets errno to EROFS - * if the file is on a read-only file system. - * 4. link() fails with -1 return value and sets errno to ELOOP - * if too many symbolic links were encountered in resolving path. + +/*\ + * Verify that: + * + * - link() fails with EPERM if the old path is a directory. + * - link() fails with EXDEV if the old path and the new path + * are not on the same mounted file system(Linux permits + * a file system to be mounted at multiple points, but link() + * does not work across different mount points, even if the same + * file system is mounted on both). + * - link() fails with EROFS if the file is on a read-only file system. + * - link() fails with ELOOP if too many symbolic links were encountered + * in resolving path. */ + #include #include "tst_test.h" diff --git a/testcases/kernel/syscalls/listmount/.gitignore b/testcases/kernel/syscalls/listmount/.gitignore new file mode 100644 index 00000000..4d709bae --- /dev/null +++ b/testcases/kernel/syscalls/listmount/.gitignore @@ -0,0 +1,4 @@ +listmount01 +listmount02 +listmount03 +listmount04 diff --git a/testcases/kernel/syscalls/listmount/Makefile b/testcases/kernel/syscalls/listmount/Makefile new file mode 100644 index 00000000..8cf1b902 --- /dev/null +++ b/testcases/kernel/syscalls/listmount/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/listmount/listmount.h b/testcases/kernel/syscalls/listmount/listmount.h new file mode 100644 index 00000000..aad927f7 --- /dev/null +++ b/testcases/kernel/syscalls/listmount/listmount.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef LISTMOUNT_H +#define LISTMOUNT_H + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/mount.h" +#include "lapi/syscalls.h" + +static inline ssize_t listmount(uint64_t mnt_id, uint64_t last_mnt_id, + uint64_t list[], size_t num, unsigned int flags) +{ + struct mnt_id_req req = { + .size = MNT_ID_REQ_SIZE_VER0, + .mnt_id = mnt_id, + .param = last_mnt_id, + }; + + return tst_syscall(__NR_listmount, &req, list, num, flags); +} + +#endif diff --git a/testcases/kernel/syscalls/listmount/listmount01.c b/testcases/kernel/syscalls/listmount/listmount01.c new file mode 100644 index 00000000..5090ae7b --- /dev/null +++ b/testcases/kernel/syscalls/listmount/listmount01.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that listmount() is properly recognizing a mounted + * root directory using LSMT_ROOT flag. + * + * [Algorithm] + * + * - move into a new unshared namespace + * - mount() a root inside temporary folder and get its mount ID + * - get list of mounted IDs using listmount(LSMT_ROOT, ..) + * - verify that the root mount ID is the only mount ID present inside the list + */ + +#define _GNU_SOURCE + +#include "listmount.h" +#include "lapi/stat.h" +#include "lapi/sched.h" + +#define MNTPOINT "mntpoint" +#define LISTSIZE 32 + +static uint64_t root_id; + +static void run(void) +{ + uint64_t list[LISTSIZE]; + + TST_EXP_POSITIVE(listmount(LSMT_ROOT, 0, list, LISTSIZE, 0)); + if (!TST_PASS) + return; + + TST_EXP_EQ_LI(TST_RET, 1); + TST_EXP_EQ_LI(list[0], root_id); +} + +static void setup(void) +{ + struct ltp_statx sx; + + SAFE_UNSHARE(CLONE_NEWNS); + + SAFE_CHROOT(MNTPOINT); + SAFE_MOUNT("", "/", NULL, MS_REC | MS_PRIVATE, NULL); + SAFE_STATX(AT_FDCWD, "/", 0, STATX_MNT_ID_UNIQUE, &sx); + + root_id = sx.data.stx_mnt_id; +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .min_kver = "6.8", + .mount_device = 1, + .mntpoint = MNTPOINT, +}; diff --git a/testcases/kernel/syscalls/listmount/listmount02.c b/testcases/kernel/syscalls/listmount/listmount02.c new file mode 100644 index 00000000..a8ee037f --- /dev/null +++ b/testcases/kernel/syscalls/listmount/listmount02.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that listmount() is properly reading groups of mount IDs, + * checking that both oneshoot and iterator API for listmount() return the same + * array. + * + * [Algorithm] + * + * - move into a new unshared namespace + * - mount() our new root inside temporary folder + * - generate a full mounts tree inside root folder, doubling the number of + * mounted filesystems each bind mount + * - read the full list of mounted IDs using listmount(LSMT_ROOT, ..) + * - read the list of mounted IDs using groups of fixed size + * - compare the first mount list with the second mount list + */ + +#include "listmount.h" +#include "lapi/sched.h" + +#define MNTPOINT "mntpoint" +#define BIND_MOUNTS 7 +#define GROUPS_SIZE 3 +#define LISTSIZE (1 << BIND_MOUNTS) + +static void run(void) +{ + ssize_t ret; + size_t id, tot_ids, count = 0; + uint64_t mount_ids[LISTSIZE]; + uint64_t list[LISTSIZE]; + + for (int i = 0; i < BIND_MOUNTS; i++) + SAFE_MOUNT("/", "/", NULL, MS_BIND, NULL); + + tst_res(TINFO, "Reading all %d mount IDs in once", LISTSIZE); + + TST_EXP_POSITIVE(listmount(LSMT_ROOT, 0, mount_ids, LISTSIZE, 0)); + if (!TST_PASS) + goto end; + + tot_ids = (size_t)TST_RET; + + if (tot_ids != LISTSIZE) { + tst_res(TFAIL, "listmount() returned %lu but %d was expected", + tot_ids, LISTSIZE); + goto end; + } + + tst_res(TINFO, "Reading groups of %d mount IDs", GROUPS_SIZE); + + while (count < LISTSIZE) { + id = count ? list[count - 1] : 0; + ret = listmount(LSMT_ROOT, id, list + count, GROUPS_SIZE, 0); + + tst_res(TDEBUG, "listmount(LSMT_ROOT, %lu, list + %lu, %d, 0)", + id, count, GROUPS_SIZE); + + if (ret == -1) { + tst_res(TFAIL, "listmount() failed with %s", tst_strerrno(errno)); + goto end; + } + + count += ret; + + if (TST_RET < GROUPS_SIZE) + break; + } + + for (size_t i = 0; i < LISTSIZE; i++) { + if (mount_ids[i] != list[i]) { + tst_res(TFAIL, "Mount ID differs at %ld index", i); + goto end; + } + } + + tst_res(TPASS, "All mount IDs have been correctly read"); + +end: + for (int i = 0; i < BIND_MOUNTS; i++) + SAFE_UMOUNT("/"); +} + +static void setup(void) +{ + SAFE_UNSHARE(CLONE_NEWNS); + SAFE_CHROOT(MNTPOINT); + + SAFE_MOUNT("", "/", NULL, MS_REC | MS_SHARED, NULL); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .forks_child = 1, + .min_kver = "6.8", + .mount_device = 1, + .mntpoint = MNTPOINT, +}; diff --git a/testcases/kernel/syscalls/listmount/listmount03.c b/testcases/kernel/syscalls/listmount/listmount03.c new file mode 100644 index 00000000..dfbf2bdd --- /dev/null +++ b/testcases/kernel/syscalls/listmount/listmount03.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that listmount() raises EPERM when mount point is not accessible. + */ + +#define _GNU_SOURCE + +#include +#include "listmount.h" +#include "lapi/stat.h" + +#define LISTSIZE 32 + +static uint64_t root_id; +static gid_t nobody_gid; +static uid_t nobody_uid; + +static void run(void) +{ + if (SAFE_FORK()) + return; + + uint64_t list[LISTSIZE]; + + SAFE_SETEGID(nobody_gid); + SAFE_SETEUID(nobody_uid); + + TST_EXP_FAIL(listmount(root_id, 0, list, LISTSIZE, 0), EPERM); + + exit(0); +} + +static void setup(void) +{ + struct ltp_statx sx; + struct passwd *pw; + + pw = SAFE_GETPWNAM("nobody"); + nobody_gid = pw->pw_gid; + nobody_uid = pw->pw_uid; + + SAFE_STATX(AT_FDCWD, "/", 0, STATX_MNT_ID_UNIQUE, &sx); + root_id = sx.data.stx_mnt_id; + + SAFE_CHROOT(tst_tmpdir_path()); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .needs_root = 1, + .needs_tmpdir = 1, + .forks_child = 1, + .min_kver = "6.8", +}; + diff --git a/testcases/kernel/syscalls/listmount/listmount04.c b/testcases/kernel/syscalls/listmount/listmount04.c new file mode 100644 index 00000000..a52bad06 --- /dev/null +++ b/testcases/kernel/syscalls/listmount/listmount04.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that listmount() raises the correct errors according with + * invalid data: + * + * - EFAULT: req or mnt_id are unaccessible memories + * - EINVAL: invalid flags or mnt_id request + * - ENOENT: non-existent mount point + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/mount.h" +#include "lapi/syscalls.h" + +#define MNT_SIZE 32 + +static struct mnt_id_req *request; +static uint64_t mnt_ids[MNT_SIZE]; + +static struct tcase { + int req_usage; + uint32_t size; + uint32_t spare; + uint64_t mnt_id; + uint64_t param; + uint64_t *mnt_ids; + size_t nr_mnt_ids; + uint64_t flags; + int exp_errno; + char *msg; +} tcases[] = { + { + .req_usage = 0, + .mnt_ids = mnt_ids, + .nr_mnt_ids = MNT_SIZE, + .exp_errno = EFAULT, + .msg = "request points to unaccessible memory", + }, + { + .req_usage = 1, + .size = MNT_ID_REQ_SIZE_VER0, + .mnt_id = LSMT_ROOT, + .mnt_ids = NULL, + .nr_mnt_ids = MNT_SIZE, + .exp_errno = EFAULT, + .msg = "mnt_ids points to unaccessible memory", + }, + { + .req_usage = 1, + .size = MNT_ID_REQ_SIZE_VER0, + .mnt_id = LSMT_ROOT, + .mnt_ids = mnt_ids, + .nr_mnt_ids = MNT_SIZE, + .flags = -1, + .exp_errno = EINVAL, + .msg = "invalid flags", + }, + { + .req_usage = 1, + .size = 0, + .mnt_id = LSMT_ROOT, + .mnt_ids = mnt_ids, + .nr_mnt_ids = MNT_SIZE, + .exp_errno = EINVAL, + .msg = "insufficient mnt_id_req.size", + }, + { + .req_usage = 1, + .size = MNT_ID_REQ_SIZE_VER0, + .spare = -1, + .mnt_id = LSMT_ROOT, + .mnt_ids = mnt_ids, + .nr_mnt_ids = MNT_SIZE, + .exp_errno = EINVAL, + .msg = "invalid mnt_id_req.spare", + }, + { + .req_usage = 1, + .size = MNT_ID_REQ_SIZE_VER0, + .mnt_id = LSMT_ROOT, + .param = STATMOUNT_PROPAGATE_FROM + 1, + .mnt_ids = mnt_ids, + .nr_mnt_ids = MNT_SIZE, + .exp_errno = EINVAL, + .msg = "invalid mnt_id_req.param", + }, + { + .req_usage = 1, + .size = MNT_ID_REQ_SIZE_VER0, + .mnt_id = 0, + .mnt_ids = mnt_ids, + .nr_mnt_ids = MNT_SIZE, + .exp_errno = EINVAL, + .msg = "invalid mnt_id_req.mnt_id", + }, + { + .req_usage = 1, + .size = MNT_ID_REQ_SIZE_VER0, + .mnt_id = LSMT_ROOT - 1, + .mnt_ids = mnt_ids, + .nr_mnt_ids = MNT_SIZE, + .exp_errno = ENOENT, + .msg = "non-existant mnt_id", + }, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + struct mnt_id_req *req = NULL; + + memset(mnt_ids, 0, sizeof(mnt_ids)); + + if (tc->req_usage) { + req = request; + req->mnt_id = tc->mnt_id; + req->param = tc->param; + req->size = tc->size; + req->spare = tc->spare; + } + + TST_EXP_FAIL(tst_syscall(__NR_listmount, req, tc->mnt_ids, + tc->nr_mnt_ids, tc->flags), tc->exp_errno, + "%s", tc->msg); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .min_kver = "6.8", + .bufs = (struct tst_buffers []) { + { &request, .size = MNT_ID_REQ_SIZE_VER0 }, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/listxattr/.gitignore b/testcases/kernel/syscalls/listxattr/.gitignore index be0675a6..0d672b6e 100755 --- a/testcases/kernel/syscalls/listxattr/.gitignore +++ b/testcases/kernel/syscalls/listxattr/.gitignore @@ -1,3 +1,4 @@ /listxattr01 /listxattr02 /listxattr03 +/listxattr04 diff --git a/testcases/kernel/syscalls/listxattr/Makefile b/testcases/kernel/syscalls/listxattr/Makefile index c2f84b15..e96bb3fa 100755 --- a/testcases/kernel/syscalls/listxattr/Makefile +++ b/testcases/kernel/syscalls/listxattr/Makefile @@ -6,4 +6,6 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk +listxattr04: LDLIBS += $(ACL_LIBS) + include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/listxattr/listxattr01.c b/testcases/kernel/syscalls/listxattr/listxattr01.c index 62198b2a..73489a77 100755 --- a/testcases/kernel/syscalls/listxattr/listxattr01.c +++ b/testcases/kernel/syscalls/listxattr/listxattr01.c @@ -1,18 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 RT-RK Institute for Computer Based Systems -* Author: Dejan Jovicevic -*/ + * Copyright (c) 2016 RT-RK Institute for Computer Based Systems + * Copyright (c) Linux Test Project, 2016-2024 + * Author: Dejan Jovicevic + */ -/* -* Test Name: listxattr01 -* -* Description: -* The testcase checks the basic functionality of the listxattr(2). -* listxattr(2) retrieves the list of extended attribute names -* associated with the file itself in the filesystem. -* -*/ +/*\ + * Test for listxattr(2) system call. + * + * listxattr(2) retrieves the list of extended attribute names + * associated with the file itself in the filesystem. + */ #include "config.h" #include diff --git a/testcases/kernel/syscalls/listxattr/listxattr02.c b/testcases/kernel/syscalls/listxattr/listxattr02.c index 98a0985b..78b48821 100755 --- a/testcases/kernel/syscalls/listxattr/listxattr02.c +++ b/testcases/kernel/syscalls/listxattr/listxattr02.c @@ -1,25 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 RT-RK Institute for Computer Based Systems -* Author: Dejan Jovicevic -*/ + * Copyright (c) 2016 RT-RK Institute for Computer Based Systems + * Copyright (c) Linux Test Project, 2016-2024 + * Author: Dejan Jovicevic + */ -/* -* Test Name: listxattr02 -* -* Description: -* 1) listxattr(2) fails if the size of the list buffer is too small -* to hold the result. -* 2) listxattr(2) fails if path is an empty string. -* 3) listxattr(2) fails when attempted to read from a invalid address. -* 4) listxattr(2) fails if path is longer than allowed. -* -* Expected Result: -* 1) listxattr(2) should return -1 and set errno to ERANGE. -* 2) listxattr(2) should return -1 and set errno to ENOENT. -* 3) listxattr(2) should return -1 and set errno to EFAULT. -* 4) listxattr(2) should return -1 and set errno to ENAMETOOLONG. -*/ +/*\ + * Test for listxattr error. + * + * - ERANGE - the size of the list buffer is too small to hold the result. + * - ENOENT - path is an empty string. + * - EFAULT - attempted to read from a invalid address. + * - ENAMETOOLONG - path is longer than allowed. + */ #include "config.h" #include @@ -38,7 +31,7 @@ #define VALUE_SIZE (sizeof(VALUE) - 1) #define TESTFILE "testfile" -char longpathname[PATH_MAX + 2]; +static char longpathname[PATH_MAX + 2]; static struct test_case { const char *path; diff --git a/testcases/kernel/syscalls/listxattr/listxattr03.c b/testcases/kernel/syscalls/listxattr/listxattr03.c index 4c1e7f16..0fe072b8 100755 --- a/testcases/kernel/syscalls/listxattr/listxattr03.c +++ b/testcases/kernel/syscalls/listxattr/listxattr03.c @@ -1,16 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 RT-RK Institute for Computer Based Systems -* Author: Dejan Jovicevic -*/ + * Copyright (c) 2016 RT-RK Institute for Computer Based Systems + * Copyright (c) Linux Test Project, 2016-2024 + * Author: Dejan Jovicevic + */ /* -* Test Name: listxattr03 -* -* Description: -* An empty buffer of size zero can return the current size of the list -* of extended attribute names, which can be used to estimate a suitable buffer. -*/ + * Test for returning the current size of the list of extended attribute names, + * with size specified as zero. + */ #include "config.h" #include diff --git a/testcases/kernel/syscalls/listxattr/listxattr04.c b/testcases/kernel/syscalls/listxattr/listxattr04.c new file mode 100644 index 00000000..41e8a447 --- /dev/null +++ b/testcases/kernel/syscalls/listxattr/listxattr04.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Andrea Cervesato + */ + +/*\ + * Test reproducer for a bug introduced in 8b0ba61df5a1 ("fs/xattr.c: fix + * simple_xattr_list to always include security.* xattrs") and fixed in + * 800d0b9b6a8b (fs/xattr.c: fix simple_xattr_list()). + * + * Bug can be reproduced when SELinux and ACL are activated on inodes as + * following: + * + * $ touch testfile + * $ setfacl -m u:myuser:rwx testfile + * $ getfattr -dm. /tmp/testfile + * Segmentation fault (core dumped) + * + * The reason why this happens is that simple_xattr_list() always includes + * security.* xattrs without resetting error flag after + * security_inode_listsecurity(). This results into an incorrect length of the + * returned xattr name if POSIX ACL is also applied on the inode. + */ + +#include "config.h" +#include "tst_test.h" + +#if defined(HAVE_SYS_XATTR_H) && defined(HAVE_LIBACL) + +#include +#include + +#define ACL_PERM "u::rw-,u:root:rwx,g::r--,o::r--,m::rwx" +#define TEST_FILE "test.bin" + +static acl_t acl; + +static void verify_xattr(const int size) +{ + char buf[size]; + + memset(buf, 0, sizeof(buf)); + + if (listxattr(TEST_FILE, buf, size) == -1) { + if (errno != ERANGE) + tst_brk(TBROK | TERRNO, "listxattr() error"); + + tst_res(TFAIL, "listxattr() failed to read attributes length: ERANGE"); + return; + } + + tst_res(TPASS, "listxattr() correctly read attributes length"); +} + +static void run(void) +{ + int size; + + size = listxattr(TEST_FILE, NULL, 0); + if (size == -1) + tst_brk(TBROK | TERRNO, "listxattr() error"); + + verify_xattr(size); +} + +static void setup(void) +{ + int res; + + if (!tst_lsm_enabled("selinux") && !tst_lsm_enabled("smack")) + tst_brk(TCONF, "There are no LSM(s) implementing xattr"); + + SAFE_TOUCH(TEST_FILE, 0644, NULL); + + acl = acl_from_text(ACL_PERM); + if (!acl) + tst_brk(TBROK | TERRNO, "acl_from_text() failed"); + + res = acl_set_file(TEST_FILE, ACL_TYPE_ACCESS, acl); + if (res == -1) { + if (errno == EOPNOTSUPP) + tst_brk(TCONF | TERRNO, "acl_set_file()"); + + tst_brk(TBROK | TERRNO, "acl_set_file(%s) failed", TEST_FILE); + } +} + +static void cleanup(void) +{ + if (acl) + acl_free(acl); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .needs_tmpdir = 1, + .tags = (const struct tst_tag[]) { + {"linux-git", "800d0b9b6a8b"}, + {} + } +}; + +#else /* HAVE_SYS_XATTR_H && HAVE_LIBACL */ + TST_TEST_TCONF(" or does not exist"); +#endif diff --git a/testcases/kernel/syscalls/llistxattr/llistxattr01.c b/testcases/kernel/syscalls/llistxattr/llistxattr01.c index f5941326..e36f244c 100755 --- a/testcases/kernel/syscalls/llistxattr/llistxattr01.c +++ b/testcases/kernel/syscalls/llistxattr/llistxattr01.c @@ -1,18 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 Fujitsu Ltd. -* Author: Xiao Yang -*/ + * Copyright (c) 2016 Fujitsu Ltd. + * Author: Xiao Yang + */ -/* -* Test Name: llistxattr01 -* -* Description: -* The testcase checks the basic functionality of the llistxattr(2). -* llistxattr(2) retrieves the list of extended attribute names -* associated with the link itself in the filesystem. -* -*/ +/*\ + * Basic test for llistxattr(2), retrieves the list of extended attribute names + * associated with the link itself in the filesystem. + */ #include "config.h" #include diff --git a/testcases/kernel/syscalls/llistxattr/llistxattr02.c b/testcases/kernel/syscalls/llistxattr/llistxattr02.c index 6974f013..6d7f716e 100755 --- a/testcases/kernel/syscalls/llistxattr/llistxattr02.c +++ b/testcases/kernel/syscalls/llistxattr/llistxattr02.c @@ -1,25 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 Fujitsu Ltd. -* Author: Xiao Yang -*/ + * Copyright (c) 2016 Fujitsu Ltd. + * Author: Xiao Yang + */ -/* -* Test Name: llistxattr02 -* -* Description: -* 1) llistxattr(2) fails if the size of the list buffer is too small -* to hold the result. -* 2) llistxattr(2) fails if path is an empty string. -* 3) llistxattr(2) fails when attempted to read from a invalid address. -* 4) llistxattr(2) fails if path is longer than allowed. -* -* Expected Result: -* 1) llistxattr(2) should return -1 and set errno to ERANGE. -* 2) llistxattr(2) should return -1 and set errno to ENOENT. -* 3) llistxattr(2) should return -1 and set errno to EFAULT. -* 4) llistxattr(2) should return -1 and set errno to ENAMETOOLONG. -*/ +/*\ + * Verify llistxattr(2) returns -1 and set proper errno: + * + * - ERANGE if the size of the list buffer is too small to hold the result + * - ENOENT if path is an empty string + * - EFAULT when attempted to read from a invalid address + * - ENAMETOOLONG if path is longer than allowed + */ #include "config.h" #include diff --git a/testcases/kernel/syscalls/llistxattr/llistxattr03.c b/testcases/kernel/syscalls/llistxattr/llistxattr03.c index 940851f4..11b3243c 100755 --- a/testcases/kernel/syscalls/llistxattr/llistxattr03.c +++ b/testcases/kernel/syscalls/llistxattr/llistxattr03.c @@ -1,17 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2016 Fujitsu Ltd. -* Author: Xiao Yang -*/ + * Copyright (c) 2016 Fujitsu Ltd. + * Author: Xiao Yang + */ -/* -* Test Name: llistxattr03 -* -* Description: -* llistxattr is identical to listxattr. an empty buffer of size zero -* can return the current size of the list of extended attribute names, -* which can be used to estimate a suitable buffer. -*/ +/*\ + * Verify that llistxattr(2) call with zero size returns the current size of the + * list of extended attribute names, which can be used to determine the size of + * the buffer that should be supplied in a subsequent llistxattr(2) call. + */ #include "config.h" #include diff --git a/testcases/kernel/syscalls/lremovexattr/lremovexattr01.c b/testcases/kernel/syscalls/lremovexattr/lremovexattr01.c index 590f5a6f..5761b142 100755 --- a/testcases/kernel/syscalls/lremovexattr/lremovexattr01.c +++ b/testcases/kernel/syscalls/lremovexattr/lremovexattr01.c @@ -4,10 +4,7 @@ * Author: Rafael David Tinoco */ -/* - * Test Name: lremovexattr01 - * - * Description: +/*\ * lremovexattr(2) removes the extended attribute identified by a name and * associated with a given path in the filesystem. Unlike removexattr(2), * lremovexattr(2) removes the attribute from the symbolic link only, and not diff --git a/testcases/kernel/syscalls/lsm/.gitignore b/testcases/kernel/syscalls/lsm/.gitignore new file mode 100644 index 00000000..467f07ce --- /dev/null +++ b/testcases/kernel/syscalls/lsm/.gitignore @@ -0,0 +1,6 @@ +lsm_get_self_attr01 +lsm_get_self_attr02 +lsm_get_self_attr03 +lsm_list_modules01 +lsm_list_modules02 +lsm_set_self_attr01 diff --git a/testcases/kernel/syscalls/lsm/Makefile b/testcases/kernel/syscalls/lsm/Makefile new file mode 100644 index 00000000..8cf1b902 --- /dev/null +++ b/testcases/kernel/syscalls/lsm/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/lsm/lsm_common.h b/testcases/kernel/syscalls/lsm/lsm_common.h new file mode 100644 index 00000000..cc063eea --- /dev/null +++ b/testcases/kernel/syscalls/lsm/lsm_common.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef LSM_GET_SELF_ATTR_H +#define LSM_GET_SELF_ATTR_H + +#include "tst_test.h" +#include "lapi/lsm.h" + +static inline struct lsm_ctx *next_ctx(struct lsm_ctx *tctx) +{ + return (struct lsm_ctx *)((char *)tctx + sizeof(*tctx) + tctx->ctx_len); +} + +static inline void read_proc_attr(const char *attr, char *val, const size_t size) +{ + int fd; + char *ptr; + char path[BUFSIZ]; + + memset(val, 0, size); + memset(path, 0, BUFSIZ); + + snprintf(path, BUFSIZ, "/proc/self/attr/%s", attr); + + tst_res(TINFO, "Reading %s", path); + + fd = SAFE_OPEN(path, O_RDONLY); + + if (read(fd, val, size) > 0) { + ptr = strchr(val, '\n'); + if (ptr) + *ptr = '\0'; + } + + SAFE_CLOSE(fd); +} + +static inline uint32_t count_supported_attr_current(void) +{ + uint32_t lsm_count = 0; + + if (tst_lsm_enabled("selinux")) + lsm_count++; + + if (tst_lsm_enabled("apparmor")) + lsm_count++; + + if (tst_lsm_enabled("smack")) + lsm_count++; + + return lsm_count; +} + +static inline uint32_t verify_supported_attr_current(void) +{ + uint32_t lsm_count; + + lsm_count = count_supported_attr_current(); + + if (!lsm_count) + tst_brk(TCONF, "LSM_ATTR_CURRENT is not supported by any LSM"); + + return lsm_count; +} +#endif diff --git a/testcases/kernel/syscalls/lsm/lsm_get_self_attr01.c b/testcases/kernel/syscalls/lsm/lsm_get_self_attr01.c new file mode 100644 index 00000000..ec272b93 --- /dev/null +++ b/testcases/kernel/syscalls/lsm/lsm_get_self_attr01.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that lsm_get_self_attr syscall is raising errors when invalid data is + * provided. + */ + +#include "lsm_common.h" + +static struct lsm_ctx *ctx; +static uint32_t ctx_size; +static uint32_t ctx_size_small; + +static struct tcase { + int attr; + struct lsm_ctx **ctx; + uint32_t *size; + uint32_t flags; + int exp_err; + char *msg; +} tcases[] = { + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx, + .exp_err = EINVAL, + .msg = "size is NULL", + }, + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx, + .size = &ctx_size, + .flags = LSM_FLAG_SINGLE | (LSM_FLAG_SINGLE << 1), + .exp_err = EINVAL, + .msg = "flags is invalid", + }, + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx, + .size = &ctx_size_small, + .exp_err = E2BIG, + .msg = "size is too smal", + }, + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx, + .size = &ctx_size, + .flags = LSM_FLAG_SINGLE, + .exp_err = EINVAL, + .msg = "flags force to use ctx attributes", + }, + { + .attr = LSM_ATTR_CURRENT | LSM_ATTR_PREV, + .ctx = &ctx, + .size = &ctx_size, + .flags = 0, + .exp_err = EOPNOTSUPP, + .msg = "flags overset", + } +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + memset(ctx, 0, LSM_CTX_SIZE_DEFAULT); + ctx_size = LSM_CTX_SIZE_DEFAULT; + ctx_size_small = 1; + + TST_EXP_FAIL(lsm_get_self_attr( + tc->attr, *tc->ctx, tc->size, tc->flags), + tc->exp_err, + "%s", tc->msg); +} + +static void setup(void) +{ + verify_supported_attr_current(); +} + +static struct tst_test test = { + .setup = setup, + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .min_kver = "6.8", + .bufs = (struct tst_buffers[]) { + {&ctx, .size = LSM_CTX_SIZE_DEFAULT}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/lsm/lsm_get_self_attr02.c b/testcases/kernel/syscalls/lsm/lsm_get_self_attr02.c new file mode 100644 index 00000000..889f3830 --- /dev/null +++ b/testcases/kernel/syscalls/lsm/lsm_get_self_attr02.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that lsm_get_self_attr syscall is acting correctly when ctx is NULL. + * The syscall can behave in different ways according to the current system + * status: + * + * - if any LSM is running inside the system, the syscall will pass and it will + * provide a size as big as the attribute + * - if no LSM(s) are running inside the system, the syscall will fail with -1 + * return code + */ +#include "lsm_common.h" + +static uint32_t page_size; +static uint32_t lsm_count; + +static void run(void) +{ + uint32_t size = page_size; + + if (lsm_count) { + TST_EXP_POSITIVE(lsm_get_self_attr( + LSM_ATTR_CURRENT, NULL, &size, 0)); + TST_EXP_EXPR(size > 1); + } else { + TST_EXP_FAIL(lsm_get_self_attr( + LSM_ATTR_CURRENT, NULL, &size, 0), EOPNOTSUPP); + } +} + +static void setup(void) +{ + page_size = SAFE_SYSCONF(_SC_PAGESIZE); + lsm_count = count_supported_attr_current(); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .min_kver = "6.8", +}; diff --git a/testcases/kernel/syscalls/lsm/lsm_get_self_attr03.c b/testcases/kernel/syscalls/lsm/lsm_get_self_attr03.c new file mode 100644 index 00000000..68d348de --- /dev/null +++ b/testcases/kernel/syscalls/lsm/lsm_get_self_attr03.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that LSM_ATTR_CURRENT attribute is correctly recognizing + * the current, active security context of the process. This is done by + * checking that /proc/self/attr/current matches with the obtained value. + */ + +#include "lsm_common.h" + +static struct lsm_ctx *ctx; +static uint32_t page_size; + +static void run(void) +{ + tst_res(TINFO, "Verifying 'LSM_ATTR_CURRENT' attribute"); + + uint32_t count; + uint32_t size = page_size; + char attr[size]; + + memset(attr, 0, size); + memset(ctx, 0, LSM_CTX_SIZE_DEFAULT); + + count = TST_EXP_POSITIVE( + lsm_get_self_attr(LSM_ATTR_CURRENT, ctx, &size, 0)); + + if (TST_RET == -1) + return; + + if (!count) { + tst_res(TFAIL, "Can't read any attribute"); + return; + } + + read_proc_attr("current", attr, page_size); + + TST_EXP_EQ_STR(attr, (char *)ctx->ctx); + + struct lsm_ctx *next = next_ctx(ctx); + + for (uint32_t i = 1; i < count; i++) { + TST_EXP_EXPR(strcmp(attr, (char *)next->ctx) != 0, + "Attribute and next LSM context must be different"); + + next = next_ctx(next); + } +} + +static void setup(void) +{ + verify_supported_attr_current(); + + page_size = SAFE_SYSCONF(_SC_PAGESIZE); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .min_kver = "6.8", + .bufs = (struct tst_buffers[]) { + {&ctx, .size = LSM_CTX_SIZE_DEFAULT}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/lsm/lsm_list_modules01.c b/testcases/kernel/syscalls/lsm/lsm_list_modules01.c new file mode 100644 index 00000000..51ff5abe --- /dev/null +++ b/testcases/kernel/syscalls/lsm/lsm_list_modules01.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that lsm_list_modules syscall is raising errors when invalid data is + * provided. + */ + +#include "lsm_common.h" + +#define MAX_LSM_NUM 32 + +static uint64_t lsm_ids[MAX_LSM_NUM]; +static uint32_t page_size; +static uint32_t ids_size; +static uint32_t ids_size_small; + +static struct tcase { + uint64_t *ids; + uint32_t *size; + uint32_t flags; + int exp_errno; + char *msg; +} tcases[] = { + { + .size = &ids_size, + .exp_errno = EFAULT, + .msg = "ids is NULL", + }, + { + .ids = lsm_ids, + .exp_errno = EFAULT, + .msg = "size is NULL", + }, + { + .ids = lsm_ids, + .size = &ids_size_small, + .exp_errno = E2BIG, + .msg = "size is too small", + }, + { + .ids = lsm_ids, + .size = &ids_size, + .flags = 1, + .exp_errno = EINVAL, + .msg = "flags must be zero", + }, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + memset(lsm_ids, 0, sizeof(lsm_ids)); + ids_size = page_size; + ids_size_small = 0; + + TST_EXP_FAIL(lsm_list_modules(tc->ids, tc->size, tc->flags), + tc->exp_errno, + "%s", tc->msg); +} + +static void setup(void) +{ + page_size = SAFE_SYSCONF(_SC_PAGESIZE); +} + +static struct tst_test test = { + .test = run, + .setup = setup, + .tcnt = ARRAY_SIZE(tcases), + .min_kver = "6.8", +}; diff --git a/testcases/kernel/syscalls/lsm/lsm_list_modules02.c b/testcases/kernel/syscalls/lsm/lsm_list_modules02.c new file mode 100644 index 00000000..91e5b7fa --- /dev/null +++ b/testcases/kernel/syscalls/lsm/lsm_list_modules02.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that lsm_list_modules syscall is correctly recognizing LSM(s) enabled + * inside the system. + * + * [Algorithm] + * + * - read enabled LSM(s) inside /sys/kernel/security/lsm file + * - collect LSM IDs using lsm_list_modules syscall + * - compare the results, verifying that LSM(s) IDs are correct + */ + +#include "lsm_common.h" + +#define MAX_LSM_NUM 32 + +struct lsm_name { + char name[BUFSIZ]; + int num; +}; + +static struct lsm_name lsm_names[MAX_LSM_NUM]; +static size_t lsm_names_count; +static uint32_t page_size; +static uint64_t *ids; +static uint32_t *size; + +static void run(void) +{ + uint32_t lsm_num; + size_t counter; + + memset(ids, 0, sizeof(uint64_t) * MAX_LSM_NUM); + *size = page_size; + + lsm_num = TST_EXP_POSITIVE(lsm_list_modules(ids, size, 0)); + + TST_EXP_EQ_LI(lsm_num, lsm_names_count); + TST_EXP_EQ_LI(*size, lsm_num * sizeof(uint64_t)); + + for (size_t i = 0; i < lsm_names_count; i++) + lsm_names[i].num = 0; + + for (uint32_t i = 0; i < lsm_num; i++) { + char *name = NULL; + + switch (ids[i]) { + case LSM_ID_CAPABILITY: + name = "capability"; + break; + case LSM_ID_SELINUX: + name = "selinux"; + break; + case LSM_ID_SMACK: + name = "smack"; + break; + case LSM_ID_TOMOYO: + name = "tomoyo"; + break; + case LSM_ID_APPARMOR: + name = "apparmor"; + break; + case LSM_ID_YAMA: + name = "yama"; + break; + case LSM_ID_LOADPIN: + name = "loadpin"; + break; + case LSM_ID_SAFESETID: + name = "safesetid"; + break; + case LSM_ID_LOCKDOWN: + name = "lockdown"; + break; + case LSM_ID_BPF: + name = "bpf"; + break; + case LSM_ID_LANDLOCK: + name = "landlock"; + break; + case LSM_ID_IMA: + name = "ima"; + break; + case LSM_ID_EVM: + name = "evm"; + break; + case LSM_ID_IPE: + name = "ipe"; + break; + default: + break; + } + + if (!name) + tst_brk(TBROK, "Unsupported LSM: %lu", ids[i]); + + for (counter = 0; counter < lsm_names_count; counter++) { + if (!strcmp(name, lsm_names[counter].name)) { + lsm_names[counter].num++; + tst_res(TPASS, "'%s' is enabled", name); + break; + } + } + + if (counter >= lsm_names_count) + tst_res(TFAIL, "'%s' has not been found", name); + } + + for (size_t i = 0; i < lsm_names_count; i++) { + if (lsm_names[i].num > 1) { + tst_res(TFAIL, "'%s' LSM has been counted %d times", + lsm_names[i].name, + lsm_names[i].num); + } + } +} + +static void setup(void) +{ + int fd; + char *ptr; + char data[BUFSIZ]; + + if (access(LSM_SYS_FILE, F_OK)) + tst_brk(TCONF, "%s file is not present", LSM_SYS_FILE); + + memset(data, 0, BUFSIZ); + + page_size = SAFE_SYSCONF(_SC_PAGESIZE); + fd = SAFE_OPEN(LSM_SYS_FILE, O_RDONLY); + SAFE_READ(0, fd, data, BUFSIZ); + SAFE_CLOSE(fd); + + ptr = strtok(data, ","); + + while (ptr) { + strcpy(lsm_names[lsm_names_count].name, ptr); + ptr = strtok(NULL, ","); + lsm_names_count++; + } +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .min_kver = "6.8", + .bufs = (struct tst_buffers []) { + {&ids, .size = sizeof(uint64_t) * MAX_LSM_NUM}, + {&size, .size = sizeof(uint32_t)}, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/lsm/lsm_set_self_attr01.c b/testcases/kernel/syscalls/lsm/lsm_set_self_attr01.c new file mode 100644 index 00000000..cde9c2e7 --- /dev/null +++ b/testcases/kernel/syscalls/lsm/lsm_set_self_attr01.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that lsm_set_self_attr syscall is raising errors when invalid data is + * provided. + */ + +#include "lsm_common.h" + +static struct lsm_ctx *ctx; +static struct lsm_ctx *ctx_orig; +static struct lsm_ctx *ctx_null; +static uint32_t ctx_size; +static uint32_t ctx_size_small; +static uint32_t ctx_size_big; +static uint32_t page_size; + +static struct tcase { + uint32_t attr; + struct lsm_ctx **ctx; + uint32_t *size; + uint32_t flags; + char *msg; +} tcases[] = { + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx_null, + .size = &ctx_size, + .msg = "ctx is NULL", + }, + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx, + .size = &ctx_size_small, + .msg = "size is too small", + }, + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx, + .size = &ctx_size_big, + .msg = "size is too big", + }, + { + .attr = LSM_ATTR_CURRENT, + .ctx = &ctx, + .size = &ctx_size, + .flags = 1, + .msg = "flags must be zero", + }, + { + .attr = -1000, + .ctx = &ctx, + .size = &ctx_size, + .msg = "attr is invalid", + } +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + /* just in case lsm_set_self_attr() pass , we won't change + * LSM configuration for the following process + */ + memcpy(ctx, ctx_orig, LSM_CTX_SIZE_DEFAULT); + + ctx_size = page_size; + ctx_size_small = 1; + ctx_size_big = ctx_size + 1; + + TST_EXP_EXPR(lsm_set_self_attr( + tc->attr, *tc->ctx, *tc->size, tc->flags) == -1, + "%s", tc->msg); +} + +static void setup(void) +{ + int ret; + uint32_t size; + + verify_supported_attr_current(); + + page_size = SAFE_SYSCONF(_SC_PAGESIZE); + size = page_size; + + ret = lsm_get_self_attr(LSM_ATTR_CURRENT, ctx_orig, &size, 0); + if (ret < 0) + tst_brk(TBROK, "Can't read LSM current attribute"); +} + +static struct tst_test test = { + .test = run, + .setup = setup, + .tcnt = ARRAY_SIZE(tcases), + .min_kver = "6.8", + .bufs = (struct tst_buffers[]) { + {&ctx, .size = LSM_CTX_SIZE_DEFAULT}, + {&ctx_orig, .size = LSM_CTX_SIZE_DEFAULT}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/lstat/.gitignore b/testcases/kernel/syscalls/lstat/.gitignore index a497a445..72cba871 100755 --- a/testcases/kernel/syscalls/lstat/.gitignore +++ b/testcases/kernel/syscalls/lstat/.gitignore @@ -2,3 +2,5 @@ /lstat01_64 /lstat02 /lstat02_64 +/lstat03 +/lstat03_64 diff --git a/testcases/kernel/syscalls/lstat/lstat01.c b/testcases/kernel/syscalls/lstat/lstat01.c index 4eaca93e..4610e88b 100755 --- a/testcases/kernel/syscalls/lstat/lstat01.c +++ b/testcases/kernel/syscalls/lstat/lstat01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * Authors: William Roske, Dave Fenner diff --git a/testcases/kernel/syscalls/lstat/lstat03.c b/testcases/kernel/syscalls/lstat/lstat03.c new file mode 100644 index 00000000..a0ff4dc9 --- /dev/null +++ b/testcases/kernel/syscalls/lstat/lstat03.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Author: David Fenner, Jon Hendrickson + * Copyright (C) 2024 Andrea Cervesato + */ + +/*\ + * This test verifies that lstat() provides correct information according + * with device, access time, block size, ownership, etc. + * The implementation provides a set of tests which are specific for each one + * of the `struct stat` used to read file and symlink information. + */ + +#include "tst_test.h" + +#define FILENAME "file.txt" +#define MNTPOINT "mntpoint" +#define SYMBNAME MNTPOINT"/file_symlink" + +static struct stat *file_stat; +static struct stat *symb_stat; + +static void run(void) +{ + SAFE_LSTAT(FILENAME, file_stat); + SAFE_LSTAT(SYMBNAME, symb_stat); + + TST_EXP_EXPR(file_stat->st_dev != symb_stat->st_dev); + TST_EXP_EXPR(file_stat->st_mode != symb_stat->st_mode); + TST_EXP_EXPR(file_stat->st_nlink != symb_stat->st_nlink); + TST_EXP_EXPR(file_stat->st_ino != symb_stat->st_ino); + TST_EXP_EXPR(file_stat->st_uid != symb_stat->st_uid); + TST_EXP_EXPR(file_stat->st_gid != symb_stat->st_gid); + TST_EXP_EXPR(file_stat->st_size != symb_stat->st_size); + TST_EXP_EXPR(file_stat->st_blocks != symb_stat->st_blocks); + TST_EXP_EXPR(file_stat->st_blksize != symb_stat->st_blksize); + TST_EXP_EXPR(file_stat->st_atime != symb_stat->st_atime); + TST_EXP_EXPR(file_stat->st_mtime != symb_stat->st_mtime); + TST_EXP_EXPR(file_stat->st_ctime != symb_stat->st_ctime); +} + +static void setup(void) +{ + char opt_bsize[32]; + const char *const fs_opts[] = {opt_bsize, NULL}; + struct stat sb; + int pagesize; + int fd; + + /* change st_blksize / st_dev */ + SAFE_STAT(".", &sb); + pagesize = sb.st_blksize == 4096 ? 1024 : 4096; + + snprintf(opt_bsize, sizeof(opt_bsize), "-b %i", pagesize); + SAFE_MKFS(tst_device->dev, tst_device->fs_type, fs_opts, NULL); + SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, 0); + + SAFE_TOUCH(FILENAME, 0777, NULL); + + /* change st_nlink */ + SAFE_LINK(FILENAME, "linked_file"); + + /* change st_uid and st_gid */ + SAFE_CHOWN(FILENAME, 1000, 1000); + + /* change st_size */ + fd = SAFE_OPEN(FILENAME, O_WRONLY, 0777); + tst_fill_fd(fd, 'a', TST_KB, 500); + SAFE_CLOSE(fd); + + /* change st_atime / st_mtime / st_ctime */ + usleep(1001000); + + SAFE_SYMLINK(FILENAME, SYMBNAME); +} + +static void cleanup(void) +{ + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .needs_root = 1, + .needs_device = 1, + .mntpoint = MNTPOINT, + .bufs = (struct tst_buffers []) { + {&file_stat, .size = sizeof(struct stat)}, + {&symb_stat, .size = sizeof(struct stat)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/madvise/.gitignore b/testcases/kernel/syscalls/madvise/.gitignore index 722ac3c3..758e601a 100755 --- a/testcases/kernel/syscalls/madvise/.gitignore +++ b/testcases/kernel/syscalls/madvise/.gitignore @@ -8,3 +8,4 @@ /madvise09 /madvise10 /madvise11 +/madvise12 diff --git a/testcases/kernel/syscalls/madvise/madvise02.c b/testcases/kernel/syscalls/madvise/madvise02.c index 90c0431c..cc3faf5c 100755 --- a/testcases/kernel/syscalls/madvise/madvise02.c +++ b/testcases/kernel/syscalls/madvise/madvise02.c @@ -58,6 +58,8 @@ static char *ptr_addr; static char *tmp_addr; static char *nonalign; +#define TCASE(adv, nm, add, ee) {adv, nm, add, ee, 0} + static struct tcase { int advice; char *name; @@ -65,19 +67,19 @@ static struct tcase { int exp_errno; int skip; } tcases[] = { - {MADV_NORMAL, "MADV_NORMAL", &nonalign, EINVAL, 0}, - {1212, "MADV_NORMAL", &file1, EINVAL, 0}, - {MADV_REMOVE, "MADV_REMOVE", &file1, EINVAL, 0}, - {MADV_DONTNEED, "MADV_DONTNEED", &file1, EINVAL, 1}, - {MADV_MERGEABLE, "MADV_MERGEABLE", &file1, EINVAL, 0}, - {MADV_UNMERGEABLE, "MADV_UNMERGEABLE", &file1, EINVAL, 0}, - {MADV_NORMAL, "MADV_NORMAL", &file2, ENOMEM, 0}, - {MADV_WILLNEED, "MADV_WILLNEED", &file2, ENOMEM, 0}, - {MADV_WILLNEED, "MADV_WILLNEED", &tmp_addr, EBADF, 0}, - {MADV_FREE, "MADV_FREE", &file1, EINVAL, 0}, - {MADV_WIPEONFORK, "MADV_WIPEONFORK", &file1, EINVAL, 0}, - {MADV_WIPEONFORK, "MADV_WIPEONFORK shared_anon", &shared_anon, EINVAL, 0}, - {MADV_WIPEONFORK, "MADV_WIPEONFORK private file backed", &file3, EINVAL, 0}, + TCASE(MADV_NORMAL, "MADV_NORMAL", &nonalign, EINVAL), + TCASE(1212, "MADV_NORMAL", &file1, EINVAL), + TCASE(MADV_REMOVE, "MADV_REMOVE", &file1, EINVAL), + TCASE(MADV_DONTNEED, "MADV_DONTNEED", &file1, EINVAL), + TCASE(MADV_MERGEABLE, "MADV_MERGEABLE", &file1, EINVAL), + TCASE(MADV_UNMERGEABLE, "MADV_UNMERGEABLE", &file1, EINVAL), + TCASE(MADV_NORMAL, "MADV_NORMAL", &file2, ENOMEM), + TCASE(MADV_WILLNEED, "MADV_WILLNEED", &file2, ENOMEM), + TCASE(MADV_WILLNEED, "MADV_WILLNEED", &tmp_addr, EBADF), + TCASE(MADV_FREE, "MADV_FREE", &file1, EINVAL), + TCASE(MADV_WIPEONFORK, "MADV_WIPEONFORK", &file1, EINVAL), + TCASE(MADV_WIPEONFORK, "MADV_WIPEONFORK shared_anon", &shared_anon, EINVAL), + TCASE(MADV_WIPEONFORK, "MADV_WIPEONFORK private file backed", &file3, EINVAL), }; static void tcases_filter(void) @@ -89,11 +91,8 @@ static void tcases_filter(void) switch (tc->advice) { case MADV_DONTNEED: -#if !defined(UCLINUX) if (mlock(file1, st.st_size) < 0) tst_brk(TBROK | TERRNO, "mlock failed"); - tc->skip = 0; -#endif /* if !defined(UCLINUX) */ break; case MADV_MERGEABLE: case MADV_UNMERGEABLE: diff --git a/testcases/kernel/syscalls/madvise/madvise03.c b/testcases/kernel/syscalls/madvise/madvise03.c index cd63affc..d27bc7cb 100644 --- a/testcases/kernel/syscalls/madvise/madvise03.c +++ b/testcases/kernel/syscalls/madvise/madvise03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Check that successful madvise(2) MADV_DONTNEED operation will result in * zero-fill-on-demand pages for anonymous private mappings. */ diff --git a/testcases/kernel/syscalls/madvise/madvise06.c b/testcases/kernel/syscalls/madvise/madvise06.c index be22318e..a9df913f 100755 --- a/testcases/kernel/syscalls/madvise/madvise06.c +++ b/testcases/kernel/syscalls/madvise/madvise06.c @@ -4,21 +4,14 @@ */ /*\ - * [Description] - * * Page fault occurs in spite that madvise(WILLNEED) system call is called * to prefetch the page. This issue is reproduced by running a program * which sequentially accesses to a shared memory and calls madvise(WILLNEED) * to the next page on a page fault. * * This bug is present in all RHEL7 versions. It looks like this was fixed in - * mainline kernel > v3.15 by the following patch: - * - * commit 55231e5c898c5c03c14194001e349f40f59bd300 - * Author: Johannes Weiner - * Date: Thu May 22 11:54:17 2014 -0700 - * - * mm: madvise: fix MADV_WILLNEED on shmem swapouts + * kernel v3.15 by + * 55231e5c898c5 ("mm: madvise: fix MADV_WILLNEED on shmem swapouts") * * Two checks are performed, the first looks at how SwapCache * changes during madvise. When the pages are dirtied, about half @@ -37,10 +30,10 @@ * our process. * * It also can reproduce the MADV_WILLNEED preformance problem. - * It was introduced since 5.9 kernel with the following commit - * e6e88712e43b ("mm: optimise madvise WILLNEED") - * and fixed since 5.10-rc5 kernel with the following commit - * 66383800df9c ("mm: fix madvise WILLNEED performance problem"). + * It was introduced in kernel 5.9 in + * e6e88712e43b ("mm: optimise madvise WILLNEED") + * and fixed since 5.10-rc5 kernel in + * 66383800df9c ("mm: fix madvise WILLNEED performance problem"). */ #include @@ -242,6 +235,7 @@ static struct tst_test test = { .setup = setup, .needs_tmpdir = 1, .needs_root = 1, + .timeout = 60, .taint_check = TST_TAINT_W | TST_TAINT_D, .save_restore = (const struct tst_path_val[]) { {"/proc/sys/vm/swappiness", NULL, diff --git a/testcases/kernel/syscalls/madvise/madvise11.c b/testcases/kernel/syscalls/madvise/madvise11.c index 3cde85ef..422589f5 100644 --- a/testcases/kernel/syscalls/madvise/madvise11.c +++ b/testcases/kernel/syscalls/madvise/madvise11.c @@ -4,11 +4,9 @@ */ /*\ - * [Description] - * * Stress a possible race condition between memory pages allocation - * and soft-offline of unrelated pages as explained in the commit: - * d4ae9916ea29 (mm: soft-offline: close the race against page allocation) + * and soft-offline of unrelated pages as explained in the commit from v4.18: + * d4ae9916ea29 (mm: soft-offline: close the race against page allocation) * * Control that soft-offlined pages get correctly replaced: with the * same content and without SIGBUS generation when accessed. @@ -128,6 +126,8 @@ static int allocate_offline(int tnum) return -1; if (madvise(ptrs[num_alloc], pagesize, MADV_SOFT_OFFLINE) == -1) { + if (errno == EBUSY) + continue; if (errno != EINVAL) tst_res(TFAIL | TERRNO, "madvise failed"); if (errno == EINVAL) @@ -307,9 +307,9 @@ static int open_unpoison_pfn(void) SAFE_CMD(cmd_modprobe, NULL, NULL); /* debugfs mount point */ - mntf = setmntent("/etc/mtab", "r"); + mntf = setmntent("/proc/mounts", "r"); if (!mntf) { - tst_brk(TBROK | TERRNO, "Can't open /etc/mtab"); + tst_brk(TBROK | TERRNO, "Can't open /proc/mounts"); return -1; } while ((mnt = getmntent(mntf)) != NULL) { @@ -426,7 +426,11 @@ static struct tst_test test = { "rmmod", NULL }, - .max_runtime = 30, + .needs_kconfigs = (const char *[]) { + "CONFIG_MEMORY_FAILURE=y", + NULL + }, + .runtime = 30, .needs_checkpoints = 1, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/madvise/madvise12.c b/testcases/kernel/syscalls/madvise/madvise12.c new file mode 100644 index 00000000..8a8be5f1 --- /dev/null +++ b/testcases/kernel/syscalls/madvise/madvise12.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Andrea Cervesato + */ + +/*\ + * Verify that MADV_GUARD_INSTALL is causing SIGSEGV when someone is accessing + * memory advised with it. + * + * This is a test for feature implemented in + * 662df3e5c376 ("mm: madvise: implement lightweight guard page mechanism") + * + * [Algorithm] + * + * - allocate a certain amount of memory + * - advise memory with MADV_GUARD_INSTALL + * - access to memory from within a child and verify it gets killed by SIGSEGV + * - release memory with MADV_GUARD_REMOVE + * - verify that memory has not been modified before child got killed + * - modify memory within a new child + * - verify that memory is accessable and child was not killed by SIGSEGV + */ + +#include "tst_test.h" +#include "lapi/mmap.h" + +#define MAP_SIZE (8 * TST_KB) + +static char *addr; + +static void run(void) +{ + pid_t pid; + int status; + + memset(addr, 0, MAP_SIZE); + + TST_EXP_PASS(madvise(addr, MAP_SIZE, MADV_GUARD_INSTALL)); + + pid = SAFE_FORK(); + if (!pid) { + tst_res(TINFO, "Modifying memory content"); + + memset(addr, 'a', MAP_SIZE); + exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) + tst_res(TPASS, "Child ended by SIGSEGV as expected"); + else + tst_res(TFAIL, "Child: %s", tst_strstatus(status)); + + TST_EXP_PASS(madvise(addr, MAP_SIZE, MADV_GUARD_REMOVE)); + + for (int i = 0; i < MAP_SIZE; i++) { + if (addr[i] == 'a') { + tst_res(TFAIL, "Memory content has been modified"); + return; + } + } + + tst_res(TPASS, "Memory content didn't change"); + + pid = SAFE_FORK(); + if (!pid) { + tst_res(TINFO, "Modifying memory content"); + + memset(addr, 'b', MAP_SIZE); + exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (!WIFSIGNALED(status)) + tst_res(TPASS, "Child ended without being signaled"); + else + tst_res(TFAIL, "Child ended with %s", tst_strstatus(status)); +} + +static void setup(void) +{ + addr = SAFE_MMAP(NULL, MAP_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); +} + +static void cleanup(void) +{ + if (addr) + SAFE_MUNMAP(addr, MAP_SIZE); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .forks_child = 1, + .min_kver = "6.13", +}; diff --git a/testcases/kernel/syscalls/mallinfo/mallinfo01.c b/testcases/kernel/syscalls/mallinfo/mallinfo01.c index 48fce013..c5c7d406 100755 --- a/testcases/kernel/syscalls/mallinfo/mallinfo01.c +++ b/testcases/kernel/syscalls/mallinfo/mallinfo01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic mallinfo() test. Refer to glibc test mallinfo2 test * https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/tst-mallinfo2.c */ diff --git a/testcases/kernel/syscalls/mallinfo/mallinfo02.c b/testcases/kernel/syscalls/mallinfo/mallinfo02.c index 6012b4e2..867079a3 100755 --- a/testcases/kernel/syscalls/mallinfo/mallinfo02.c +++ b/testcases/kernel/syscalls/mallinfo/mallinfo02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic mallinfo() test for malloc() using sbrk or mmap. * It size > MMAP_THRESHOLD, it will use mmap. Otherwise, use sbrk. */ diff --git a/testcases/kernel/syscalls/mallinfo2/mallinfo2_01.c b/testcases/kernel/syscalls/mallinfo2/mallinfo2_01.c index 90cf4fcb..60e0e8af 100755 --- a/testcases/kernel/syscalls/mallinfo2/mallinfo2_01.c +++ b/testcases/kernel/syscalls/mallinfo2/mallinfo2_01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic mallinfo2() test. * * Test hblkhd member of struct mallinfo2 whether overflow when setting 2G size. @@ -26,7 +24,7 @@ void test_mallinfo2(void) char *buf; size_t size = 2UL * 1024UL * 1024UL * 1024UL; - buf = malloc(size); + LTP_VAR_USED(buf) = malloc(size); if (!buf) tst_brk(TCONF, "Current system can not malloc 2G space, skip it"); diff --git a/testcases/kernel/syscalls/mallopt/mallopt01.c b/testcases/kernel/syscalls/mallopt/mallopt01.c index 8ff4649e..77cd18a7 100755 --- a/testcases/kernel/syscalls/mallopt/mallopt01.c +++ b/testcases/kernel/syscalls/mallopt/mallopt01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Basic mallinfo() and mallopt() testing. */ diff --git a/testcases/kernel/syscalls/mbind/Makefile b/testcases/kernel/syscalls/mbind/Makefile index ed7d4375..2a7d3569 100755 --- a/testcases/kernel/syscalls/mbind/Makefile +++ b/testcases/kernel/syscalls/mbind/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS=ltpnuma +LTPLIBS = numa include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/memcmp/memcmp01.c b/testcases/kernel/syscalls/memcmp/memcmp01.c index 836cf404..38f121bd 100755 --- a/testcases/kernel/syscalls/memcmp/memcmp01.c +++ b/testcases/kernel/syscalls/memcmp/memcmp01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * The testcase for buffer comparison by check boundary conditions. */ diff --git a/testcases/kernel/syscalls/memcpy/memcpy01.c b/testcases/kernel/syscalls/memcpy/memcpy01.c index 0b64d70e..b19f4145 100755 --- a/testcases/kernel/syscalls/memcpy/memcpy01.c +++ b/testcases/kernel/syscalls/memcpy/memcpy01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * The testcase for buffer copy by check boundary conditions. */ diff --git a/testcases/kernel/syscalls/memfd_create/memfd_create04.c b/testcases/kernel/syscalls/memfd_create/memfd_create04.c index 585f17e1..8a12a72f 100755 --- a/testcases/kernel/syscalls/memfd_create/memfd_create04.c +++ b/testcases/kernel/syscalls/memfd_create/memfd_create04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Validating memfd_create() with MFD_HUGETLB and MFD_HUGE_x flags. * * Attempt to create files in the hugetlbfs filesystem using different huge page diff --git a/testcases/kernel/syscalls/memset/memset01.c b/testcases/kernel/syscalls/memset/memset01.c index 17ae42c0..5a34eb40 100755 --- a/testcases/kernel/syscalls/memset/memset01.c +++ b/testcases/kernel/syscalls/memset/memset01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * The testcase for test setting of buffer by check boundary conditions. */ diff --git a/testcases/kernel/syscalls/migrate_pages/migrate_pages03.c b/testcases/kernel/syscalls/migrate_pages/migrate_pages03.c index 4d3299b6..59d7e79d 100755 --- a/testcases/kernel/syscalls/migrate_pages/migrate_pages03.c +++ b/testcases/kernel/syscalls/migrate_pages/migrate_pages03.c @@ -139,7 +139,7 @@ static void migrate_test(void) } static struct tst_test test = { - .max_runtime = 300, + .runtime = 300, .needs_root = 1, .setup = setup, .cleanup = cleanup, diff --git a/testcases/kernel/syscalls/mincore/mincore02.c b/testcases/kernel/syscalls/mincore/mincore02.c index ab73f4bf..af3549ac 100755 --- a/testcases/kernel/syscalls/mincore/mincore02.c +++ b/testcases/kernel/syscalls/mincore/mincore02.c @@ -1,136 +1,69 @@ -/* - * Copyright (c) International Business Machines Corp., 2001 - * Author: Rajeev Tiwari: rajeevti@in.ibm.com - * Copyright (c) 2004 Gernot Payer - * Copyright (c) 2013 Cyril Hrubis - * - * 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 - */ +// SPDX-License-Identifier: GPL-2.0-or-later /* + * Copyright (c) International Business Machines Corp., 2001 + * Author: Rajeev Tiwari: rajeevti@in.ibm.com + * Copyright (c) 2004 Gernot Payer + * Copyright (c) 2013 Cyril Hrubis + * Copyright (C) 2024 SUSE LLC Andrea Manzini + */ + +/*\ * This test case provides a functional validation for mincore system call. * We mmap a file of known size (multiple of page size) and lock it in * memory. Then we obtain page location information via mincore and compare * the result with the expected value. */ -#include -#include -#include -#include -#include -#include -#include -#include +#include "tst_test.h" -#include "test.h" -#include "safe_macros.h" +#define NUM_PAGES 4 -char *TCID = "mincore02"; -int TST_TOTAL = 1; - -static int fd = 0; -static void *addr = NULL; -static int page_size; -static int num_pages = 4; -static unsigned char *vec = NULL; +static void *addr; +static int size; +static unsigned char vec[NUM_PAGES]; +static int fd; static void cleanup(void) { - free(vec); - munlock(addr, page_size * num_pages); - munmap(addr, page_size * num_pages); - close(fd); - tst_rmdir(); + if (addr) { + SAFE_MUNLOCK(addr, size); + SAFE_MUNMAP(addr, size); + } + if (fd > 0) + SAFE_CLOSE(fd); } static void setup(void) { - char *buf; - size_t size; - - tst_tmpdir(); - - page_size = getpagesize(); - if (page_size == -1) - tst_brkm(TBROK | TERRNO, cleanup, "Unable to get page size"); - - size = page_size * num_pages; - buf = malloc(size); + size = getpagesize() * NUM_PAGES; + char buf[size]; memset(buf, 42, size); - vec = malloc((size + page_size - 1) / page_size); - - fd = SAFE_OPEN(cleanup, "mincore02", O_CREAT | O_RDWR, - S_IRUSR | S_IWUSR); - /* fill the temporary file with two pages of data */ - if (write(fd, buf, size) < 0) { - tst_brkm(TBROK | TERRNO, cleanup, - "Error in writing to the file"); - } - free(buf); + int fd = SAFE_OPEN("mincore02", O_CREAT | O_RDWR, 0600); - addr = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_SHARED, fd, 0); + SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, size); + addr = SAFE_MMAP(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0); - if (addr == MAP_FAILED) { - tst_brkm(TBROK | TERRNO, cleanup, - "Unable to map file for read/write"); - } - - /* lock mmapped file, so mincore returns "in core" for all pages */ - if (mlock(addr, size) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "Unable to lock the file"); + SAFE_MLOCK(addr, size); } -int main(int argc, char **argv) +static void check_mincore(void) { - int lock_pages, counter; - int lc; + TST_EXP_PASS(mincore(addr, size, vec)); - tst_parse_opts(argc, argv, NULL, NULL); + int locked_pages = 0; - setup(); + for (int i = 0; i < NUM_PAGES; i++) + locked_pages += (vec[i] & 1); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - if (mincore(addr, num_pages * page_size, vec) == -1) { - tst_brkm(TBROK | TERRNO, cleanup, - "Unable to execute mincore system call"); - } - - /* check status of pages */ - lock_pages = 0; - - for (counter = 0; counter < num_pages; counter++) { - if (vec[counter] & 1) - lock_pages++; - } - - if (lock_pages == num_pages) { - tst_resm(TPASS, "%d pages locked, %d pages in-core", num_pages, - lock_pages); - } else { - tst_resm(TFAIL, - "not all locked pages are in-core: no. locked: %d, no. in-core: %d", - num_pages, lock_pages); - } - } - - cleanup(); - tst_exit(); + TST_EXP_EQ_SZ(locked_pages, NUM_PAGES); } + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = check_mincore, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/mkdir/mkdir02.c b/testcases/kernel/syscalls/mkdir/mkdir02.c index 7fa055b8..519c243e 100755 --- a/testcases/kernel/syscalls/mkdir/mkdir02.c +++ b/testcases/kernel/syscalls/mkdir/mkdir02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that new directory created by mkdir(2) inherites the group ID from * the parent directory and S_ISGID bit, if the S_ISGID bit is set in the * parent directory. diff --git a/testcases/kernel/syscalls/mkdir/mkdir03.c b/testcases/kernel/syscalls/mkdir/mkdir03.c index d5141bb6..7bbb76e9 100755 --- a/testcases/kernel/syscalls/mkdir/mkdir03.c +++ b/testcases/kernel/syscalls/mkdir/mkdir03.c @@ -1,14 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) Linux Test Project, 2002-2024 * Ported to LTP: Wayne Boyer */ -/* - * DESCRIPTION - * check mkdir() with various error conditions that should produce - * EFAULT, ENAMETOOLONG, EEXIST, ENOENT, ENOTDIR, ELOOP and EROFS + +/*\ + * Check mkdir() with various error conditions that should produce + * EFAULT, ENAMETOOLONG, EEXIST, ENOENT, ENOTDIR, ELOOP and EROFS. + * + * Testing on various types of files (symlinks, directories, pipes, devices, etc). */ +#include +#include #include #include #include @@ -19,6 +24,10 @@ #include "tst_test.h" #define TST_EEXIST "tst_eexist" +#define TST_PIPE "tst_pipe" +#define TST_FOLDER "tst_folder" +#define TST_SYMLINK "tst_symlink" +#define TST_NULLDEV _PATH_DEVNULL #define TST_ENOENT "tst_enoent/tst" #define TST_ENOTDIR_FILE "tst_enotdir" #define TST_ENOTDIR_DIR "tst_enotdir/tst" @@ -41,6 +50,10 @@ static struct tcase { {NULL, EFAULT}, {long_dir, ENAMETOOLONG}, {TST_EEXIST, EEXIST}, + {TST_FOLDER, EEXIST}, + {TST_PIPE, EEXIST}, + {TST_SYMLINK, EEXIST}, + {TST_NULLDEV, EEXIST}, {TST_ENOENT, ENOENT}, {TST_ENOTDIR_DIR, ENOTDIR}, {loop_dir, ELOOP}, @@ -71,6 +84,10 @@ static void setup(void) { unsigned int i; + SAFE_SYMLINK(tst_tmpdir_path(), TST_SYMLINK); + + SAFE_MKFIFO(TST_PIPE, 0777); + SAFE_MKDIR(TST_FOLDER, 0777); SAFE_TOUCH(TST_EEXIST, MODE, NULL); SAFE_TOUCH(TST_ENOTDIR_FILE, MODE, NULL); diff --git a/testcases/kernel/syscalls/mkdir/mkdir09.c b/testcases/kernel/syscalls/mkdir/mkdir09.c index 44a2348b..8f995c96 100755 --- a/testcases/kernel/syscalls/mkdir/mkdir09.c +++ b/testcases/kernel/syscalls/mkdir/mkdir09.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Create multiple processes which create subdirectories in the * same directory multiple times within test time. */ diff --git a/testcases/kernel/syscalls/mkdirat/mkdirat01.c b/testcases/kernel/syscalls/mkdirat/mkdirat01.c index a323ed5b..ca536bda 100755 --- a/testcases/kernel/syscalls/mkdirat/mkdirat01.c +++ b/testcases/kernel/syscalls/mkdirat/mkdirat01.c @@ -35,7 +35,6 @@ #include #include #include "test.h" -#include "lapi/mkdirat.h" #include "safe_macros.h" static void setup(void); diff --git a/testcases/kernel/syscalls/mkdirat/mkdirat02.c b/testcases/kernel/syscalls/mkdirat/mkdirat02.c index ebc0ed16..2bd8fe9c 100755 --- a/testcases/kernel/syscalls/mkdirat/mkdirat02.c +++ b/testcases/kernel/syscalls/mkdirat/mkdirat02.c @@ -11,7 +11,6 @@ #define _GNU_SOURCE #include "tst_test.h" -#include "lapi/mkdirat.h" #define MNT_POINT "mntpoint" #define TEST_DIR "mntpoint/test_dir" diff --git a/testcases/kernel/syscalls/mknod/mknod01.c b/testcases/kernel/syscalls/mknod/mknod01.c index 7a4d5b43..8406d26d 100755 --- a/testcases/kernel/syscalls/mknod/mknod01.c +++ b/testcases/kernel/syscalls/mknod/mknod01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * AUTHOR: William Roske, CO-PILOT: Dave Fenner @@ -6,12 +6,11 @@ */ /*\ - * [Description] - * * Verify that mknod(2) successfully creates a filesystem node with * various modes. */ +#include #include #include "tst_test.h" @@ -28,7 +27,6 @@ static int tcases[] = { S_IFREG | 06700, }; - static void run(unsigned int i) { dev_t dev = 0; @@ -37,8 +35,8 @@ static void run(unsigned int i) dev = makedev(1, 3); TST_EXP_PASS(mknod(PATH, tcases[i], dev), - "mknod(PATH, %o, %ld)", - tcases[i], dev); + "mknod(PATH, %o, %ju)", + tcases[i], (uintmax_t)dev); SAFE_UNLINK(PATH); } diff --git a/testcases/kernel/syscalls/mknod/mknod02.c b/testcases/kernel/syscalls/mknod/mknod02.c index b1885fed..bd476fff 100755 --- a/testcases/kernel/syscalls/mknod/mknod02.c +++ b/testcases/kernel/syscalls/mknod/mknod02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that if mknod(2) creates a filesystem node in a directory which * does not have the set-group-ID bit set, new node will not inherit the * group ownership from its parent directory and its group ID will be the @@ -22,7 +20,7 @@ #define MODE_SGID 02000 #define TEMP_DIR "testdir" -#define TEMP_NODE "testnode" +#define TEMP_NODE TEMP_DIR "/testnode" static struct stat buf; static struct passwd *user_nobody; @@ -39,14 +37,12 @@ static void setup(void) static void run(void) { - SAFE_CHDIR(TEMP_DIR); TST_EXP_PASS(mknod(TEMP_NODE, MODE1, 0), "mknod(%s, %o, 0)", TEMP_NODE, MODE1); SAFE_STAT(TEMP_NODE, &buf); TST_EXP_EQ_LI(buf.st_gid, 0); SAFE_UNLINK(TEMP_NODE); - SAFE_CHDIR(".."); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/mknod/mknod03.c b/testcases/kernel/syscalls/mknod/mknod03.c index 7ecadb5b..8cb9be92 100755 --- a/testcases/kernel/syscalls/mknod/mknod03.c +++ b/testcases/kernel/syscalls/mknod/mknod03.c @@ -1,296 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: mknod03 - * - * Test Description: - * Verify that mknod(2) succeeds when used to create a filesystem - * node with set group-ID bit set on a directory with set group-ID bit set. - * The node created should have set group-ID bit set and its gid should be - * equal to the effective gid of the process. - * - * Expected Result: - * mknod() should return value 0 on success and node created should have - * set group-ID bit set, its gid should be equal to the effective gid of - * the process. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * mknod03 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * This test should be run by 'super-user' (root) only. - * + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that mknod(2) succeeds when used to create a filesystem node with + * set-group-ID bit set on a directory with set-group-ID bit set. The node + * created should have set-group-ID bit set and its gid should be equal to + * the "nobody" gid. */ -#include -#include -#include -#include -#include -#include #include -#include -#include +#include "tst_uid.h" +#include "tst_test.h" -#include "test.h" -#include "safe_macros.h" +#define MODE_RWX 0777 +#define MODE_FIFO_SGID (S_IFIFO | S_ISGID | 0777) -#define LTPUSER "nobody" -#define MODE_RWX S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO -#define MODE_SGID S_IFIFO | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO -#define DIR_TEMP "testdir_3" -#define TNODE "tnode_%d" +#define TEMP_DIR "testdir" +#define TEMP_NODE TEMP_DIR "/testnode" -struct stat buf; /* struct. to hold stat(2) o/p contents */ -struct passwd *user1; /* struct. to hold getpwnam(3) o/p contents */ +static uid_t nobody_uid; +static gid_t nobody_gid, free_gid; -char *TCID = "mknod03"; -int TST_TOTAL = 1; -char node_name[PATH_MAX]; /* buffer to hold node name created */ - -gid_t group1_gid, group2_gid, mygid; /* user and process group id's */ -uid_t save_myuid, user1_uid; /* user and process user id's */ -pid_t mypid; /* process id */ - -void setup(); /* setup function for the test */ -void cleanup(); /* cleanup function for the test */ - -int main(int ac, char **av) +static void run(void) { - int lc; - int fflag; + struct stat buf; - tst_parse_opts(ac, av, NULL, NULL); + SAFE_MKNOD(TEMP_NODE, MODE_FIFO_SGID, 0); - setup(); + SAFE_STAT(TEMP_NODE, &buf); + TST_EXP_EQ_LI(buf.st_gid, free_gid); - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Attempt to create a filesystem node with group id (sgid) - * bit set on a directory with group id (sgid) bit set - * such that, the node created by mknod(2) should have - * group id (sgid) bit set and node's gid should be equal - * to that of effective gid of the process. - */ - TEST(mknod(node_name, MODE_SGID, 0)); - - /* Check return code from mknod(2) */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "mknod(%s, %#o, 0) failed, errno=%d : " - "%s", node_name, MODE_SGID, TEST_ERRNO, - strerror(TEST_ERRNO)); - continue; - } - /* Set the functionality flag */ - fflag = 1; - - /* Check for node's creation */ - if (stat(node_name, &buf) < 0) { - tst_resm(TFAIL, "stat() of %s failed, errno:%d", - node_name, TEST_ERRNO); - /* unset functionality flag */ - fflag = 0; - } - - /* - * Skip S_ISGID check - * 0fa3ecd87848 ("Fix up non-directory creation in SGID directories") - * clears S_ISGID for files created by non-group members - */ - - /* Verify group ID */ - if (buf.st_gid != group2_gid) { - tst_resm(TFAIL, "%s: Incorrect group", - node_name); - /* unset flag as functionality fails */ - fflag = 0; - } - if (fflag) { - tst_resm(TPASS, "Functionality of mknod(%s, " - "%#o, 0) successful", - node_name, MODE_SGID); - } - - /* Remove the node for the next go `round */ - if (unlink(node_name) == -1) { - tst_resm(TWARN, "unlink(%s) failed, errno:%d %s", - node_name, errno, strerror(errno)); - } - } - - /* Change the directory back to temporary directory */ - SAFE_CHDIR(cleanup, ".."); - - /* - * Invoke cleanup() to delete the test directories created - * in the setup() and exit main(). - */ - cleanup(); - - tst_exit(); + SAFE_UNLINK(TEMP_NODE); } -/* - * setup(void) - performs all ONE TIME setup for this test. - * Exit the test program on receipt of unexpected signals. - * Create a temporary directory used to hold test directories created - * and change the directory to it. - * Verify that pid of process executing the test is root. - * Create a test directory on temporary directory and set the ownership - * of test directory to guest user and process, change mode permissions - * to set group id bit on it. - * Set the effective uid/gid of the process to that of guest user. - */ -void setup(void) +static void setup(void) { - tst_require_root(); + struct passwd *ltpuser = SAFE_GETPWNAM("nobody"); - /* Capture unexpected signals */ - tst_sig(NOFORK, DEF_HANDLER, cleanup); + nobody_uid = ltpuser->pw_uid; + nobody_gid = ltpuser->pw_gid; + free_gid = tst_get_free_gid(nobody_gid); - TEST_PAUSE; + SAFE_MKDIR(TEMP_DIR, MODE_RWX); + SAFE_CHOWN(TEMP_DIR, nobody_uid, free_gid); + SAFE_CHMOD(TEMP_DIR, MODE_FIFO_SGID); - /* Make a temp dir and cd to it */ - tst_tmpdir(); - - /* fix permissions on the tmpdir */ - if (chmod(".", 0711) != 0) { - tst_brkm(TBROK, cleanup, "chmod() failed"); - } - - /* Save the real user id of the current test process */ - save_myuid = getuid(); - /* Save the process id of the current test process */ - mypid = getpid(); - - /* Get the node name to be created in the test */ - sprintf(node_name, TNODE, mypid); - - /* Get the uid/gid of ltpuser user */ - if ((user1 = getpwnam(LTPUSER)) == NULL) { - tst_brkm(TBROK, cleanup, "%s not in /etc/passwd", LTPUSER); - } - user1_uid = user1->pw_uid; - group1_gid = user1->pw_gid; - - /* Get the effective group id of the test process */ - group2_gid = getegid(); - - /* - * Create a test directory under temporary directory with the - * specified mode permissions, with uid/gid set to that of guest - * user and the test process. - */ - SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX); - SAFE_CHOWN(cleanup, DIR_TEMP, user1_uid, group2_gid); - SAFE_CHMOD(cleanup, DIR_TEMP, MODE_SGID); - - /* - * Verify that test directory created with expected permission modes - * and ownerships. - */ - SAFE_STAT(cleanup, DIR_TEMP, &buf); - - /* Verify modes of test directory */ - if (!(buf.st_mode & S_ISGID)) { - tst_brkm(TBROK, cleanup, - "%s: Incorrect modes, setgid bit not set", DIR_TEMP); - } - - /* Verify group ID of test directory */ - if (buf.st_gid != group2_gid) { - tst_brkm(TBROK, cleanup, "%s: Incorrect group", DIR_TEMP); - } - - /* - * Set the effective group id and user id of the test process - * to that of guest user (nobody) - */ - SAFE_SETGID(cleanup, group1_gid); - if (setreuid(-1, user1_uid) < 0) { - tst_brkm(TBROK, cleanup, - "Unable to set process uid to that of ltp user"); - } - - /* Save the real group ID of the current process */ - mygid = getgid(); - - /* Change directory to DIR_TEMP */ - SAFE_CHDIR(cleanup, DIR_TEMP); + SAFE_SETGID(nobody_gid); + SAFE_SETREUID(-1, nobody_uid); } -/* - * cleanup() - Performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Print test timing stats and errno log if test executed with options. - * Restore the real/effective user id of the process changed during - * setup(). - * Remove temporary directory and sub-directories/files under it - * created during setup(). - * Exit the test program with normal exit code. - */ -void cleanup(void) -{ - - /* - * Restore the effective uid of the process changed in the - * setup(). - */ - if (setreuid(-1, save_myuid) < 0) { - tst_brkm(TBROK, NULL, - "resetting process real/effective uid failed"); - } - - tst_rmdir(); - -} +static struct tst_test test = { + .setup = setup, + .test_all = run, + .needs_root = 1, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/mknod/mknod04.c b/testcases/kernel/syscalls/mknod/mknod04.c index e0123ec0..441894b3 100755 --- a/testcases/kernel/syscalls/mknod/mknod04.c +++ b/testcases/kernel/syscalls/mknod/mknod04.c @@ -1,301 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: mknod04 - * - * Test Description: - * Verify that mknod(2) succeeds when used to create a filesystem - * node on a directory with set group-ID bit set. - * The node created should not have group-ID bit set and its gid should be - * equal to the effective gid of the process. - * - * Expected Result: - * mknod() should return value 0 on success and node created should not - * have set group-ID bit set and its gid should be equal to the effective - * gid of the process. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * mknod04 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * This test should be run by 'super-user' (root) only. - * + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that mknod(2) succeeds when used to create a filesystem node on a + * directory with set-group-ID bit set. The node created should not have + * set-group-ID bit set and its gid should be equal to the effective + * gid of the process. */ -#include -#include -#include -#include -#include -#include #include -#include -#include +#include "tst_uid.h" +#include "tst_test.h" -#include "test.h" -#include "safe_macros.h" +#define MODE_RWX 0777 +#define MODE_FIFO (S_IFIFO | 0777) +#define MODE_FIFO_SGID (S_IFIFO | S_ISGID | 0777) -#define LTPUSER "nobody" -#define MODE_RWX S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO -#define MODE_SGID S_IFIFO | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO -#define DIR_TEMP "testdir_4" -#define TNODE "tnode_%d" +#define TEMP_DIR "testdir" +#define TEMP_NODE TEMP_DIR "/testnode" -struct stat buf; /* struct. to hold stat(2) o/p contents */ -struct passwd *user1; /* struct. to hold getpwnam(3) o/p contents */ +static uid_t nobody_uid; +static gid_t nobody_gid, free_gid; -char *TCID = "mknod04"; -int TST_TOTAL = 1; -char node_name[PATH_MAX]; /* buffer to hold node name created */ - -gid_t group1_gid, group2_gid, mygid; /* user and process group id's */ -uid_t save_myuid, user1_uid; /* user and process user id's */ -pid_t mypid; /* process id */ - -void setup(); /* setup function for the test */ -void cleanup(); /* cleanup function for the test */ - -int main(int ac, char **av) +static void run(void) { - int lc; - int fflag; + struct stat buf; - tst_parse_opts(ac, av, NULL, NULL); + SAFE_MKNOD(TEMP_NODE, MODE_FIFO, 0); - setup(); + SAFE_STAT(TEMP_NODE, &buf); + TST_EXP_EQ_LI(buf.st_mode & S_ISGID, 0); + TST_EXP_EQ_LI(buf.st_gid, free_gid); - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * TEST CASE CONDITION: - * Attempt to create a filesystem node on a directory - * with group id (sgid) bit set such that, - * the node created by mknod(2) should not have group id - * (sgid) bit set and node's gid should be equal to the - * effective gid of the process. - */ - TEST(mknod(node_name, MODE_RWX, 0)); - - /* Check return code from mknod(2) */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "mknod(%s, %#o, 0) failed, errno=%d : " - "%s", node_name, MODE_RWX, TEST_ERRNO, - strerror(TEST_ERRNO)); - continue; - } - /* Set the functionality flag */ - fflag = 1; - - /* Check for node's creation */ - if (stat(node_name, &buf) < 0) { - tst_resm(TFAIL, "stat() of %s failed, errno:%d", - node_name, TEST_ERRNO); - /* unset fflag */ - fflag = 0; - } - - /* Verify mode permissions of node */ - if (buf.st_mode & S_ISGID) { - tst_resm(TFAIL, "%s: Incorrect modes, setgid " - "bit set", node_name); - /* unset flag as functionality fails */ - fflag = 0; - } - - /* Verify group ID of node */ - if (buf.st_gid != group2_gid) { - tst_resm(TFAIL, "%s: Incorrect group", - node_name); - /* unset flag as functionality fails */ - fflag = 0; - } - if (fflag) { - tst_resm(TPASS, "Functionality of mknod(%s, " - "%#o, 0) successful", - node_name, MODE_RWX); - } - - /* Remove the node for the next go `round */ - if (unlink(node_name) == -1) { - tst_resm(TWARN, "unlink(%s) failed, errno:%d %s", - node_name, errno, strerror(errno)); - } - } - - /* Change the directory back to temporary directory */ - SAFE_CHDIR(cleanup, ".."); - - /* - * Invoke cleanup() to delete the test directories created - * in the setup() and exit main(). - */ - cleanup(); - - tst_exit(); + SAFE_UNLINK(TEMP_NODE); } -/* - * void - * setup(void) - performs all ONE TIME setup for this test. - * Exit the test program on receipt of unexpected signals. - * Create a temporary directory used to hold test directories created - * and change the directory to it. - * Verify that pid of process executing the test is root. - * Create a test directory on temporary directory and set the ownership - * of test directory to guest user and process, change mode permissions - * to set group-id bit on it. - * Set the effective uid/gid of the process to that of guest user. - */ -void setup(void) +static void setup(void) { - tst_require_root(); + struct passwd *ltpuser = SAFE_GETPWNAM("nobody"); - /* Capture unexpected signals */ - tst_sig(NOFORK, DEF_HANDLER, cleanup); + nobody_uid = ltpuser->pw_uid; + nobody_gid = ltpuser->pw_gid; + free_gid = tst_get_free_gid(nobody_gid); - TEST_PAUSE; + SAFE_MKDIR(TEMP_DIR, MODE_RWX); + SAFE_CHOWN(TEMP_DIR, nobody_uid, free_gid); + SAFE_CHMOD(TEMP_DIR, MODE_FIFO_SGID); - /* Make a temp dir and cd to it */ - tst_tmpdir(); - - /* fix permissions on the tmpdir */ - if (chmod(".", 0711) != 0) { - tst_brkm(TBROK, cleanup, "chmod() failed"); - } - - /* Save the real user id of the current test process */ - save_myuid = getuid(); - - /* Save the process id of the current test process */ - mypid = getpid(); - - /* Get the node name to be created in the test */ - sprintf(node_name, TNODE, mypid); - - /* Get the uid/gid of ltp user */ - if ((user1 = getpwnam(LTPUSER)) == NULL) { - tst_brkm(TBROK, cleanup, "%s not in /etc/passwd", LTPUSER); - } - user1_uid = user1->pw_uid; - group1_gid = user1->pw_gid; - - /* Get the effective group id of the test process */ - group2_gid = getegid(); - - /* - * Create a test directory under temporary directory with the - * specified mode permissions, with uid/gid set to that of guest - * user and the test process. - */ - SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX); - SAFE_CHOWN(cleanup, DIR_TEMP, user1_uid, group2_gid); - SAFE_CHMOD(cleanup, DIR_TEMP, MODE_SGID); - - /* - * Verify that test directory created with expected permission modes - * and ownerships. - */ - SAFE_STAT(cleanup, DIR_TEMP, &buf); - - /* Verify modes of test directory */ - if (!(buf.st_mode & S_ISGID)) { - tst_brkm(TBROK, cleanup, - "%s: Incorrect modes, setgid bit not set", DIR_TEMP); - } - - /* Verify group ID */ - if (buf.st_gid != group2_gid) { - tst_brkm(TBROK, cleanup, "%s: Incorrect group", DIR_TEMP); - } - - /* - * Set the effective group id and user id of the test process - * to that of guest user (nobody) - */ - SAFE_SETGID(cleanup, group1_gid); - if (setreuid(-1, user1_uid) < 0) { - tst_brkm(TBROK, cleanup, - "Unable to set process uid to that of ltp user"); - } - - /* Save the real group ID of the current process */ - mygid = getgid(); - - /* Change directory to DIR_TEMP */ - SAFE_CHDIR(cleanup, DIR_TEMP); + SAFE_SETGID(nobody_gid); + SAFE_SETREUID(-1, nobody_uid); } -/* - * cleanup() - Performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Print test timing stats and errno log if test executed with options. - * Restore the real/effective user id of the process changed during - * setup(). - * Remove temporary directory and sub-directories/files under it - * created during setup(). - * Exit the test program with normal exit code. - */ -void cleanup(void) -{ - - /* - * Restore the effective uid of the process changed in the - * setup(). - */ - if (setreuid(-1, save_myuid) < 0) { - tst_brkm(TBROK, cleanup, - "resetting process real/effective uid failed"); - } - - tst_rmdir(); - -} +static struct tst_test test = { + .setup = setup, + .test_all = run, + .needs_root = 1, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/mknod/mknod05.c b/testcases/kernel/syscalls/mknod/mknod05.c index fbc55754..bfac9227 100755 --- a/testcases/kernel/syscalls/mknod/mknod05.c +++ b/testcases/kernel/syscalls/mknod/mknod05.c @@ -1,268 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: mknod05 - * - * Test Description: - * Verify that mknod(2) succeeds when used by root to create a filesystem - * node with set group-ID bit set on a directory with set group-ID bit set. - * The node created should have set group-ID bit set and its gid should be - * equal to that of its parent directory. - * - * Expected Result: - * mknod() should return value 0 on success and node created should have - * set group-ID bit set and its gid should be equal to that of its parent - * directory. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * mknod05 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * This test should be run by 'super-user' (root) only. - * + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that mknod(2) succeeds when used to create a filesystem node with + * set-group-ID bit set on a directory with set-group-ID bit set. The node + * created should have set-group-ID bit set and its gid should be equal to + * that of its parent directory. */ -#include -#include -#include -#include -#include -#include #include -#include -#include +#include "tst_uid.h" +#include "tst_test.h" -#include "test.h" -#include "safe_macros.h" +#define MODE_RWX 0777 +#define MODE_FIFO_SGID (S_IFIFO | S_ISGID | 0777) -#define LTPUSER "nobody" -#define MODE_RWX S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO -#define MODE_SGID S_IFIFO | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO -#define DIR_TEMP "testdir_5" -#define TNODE "tnode_%d" +#define TEMP_DIR "testdir" +#define TEMP_NODE TEMP_DIR "/testnode" -struct stat buf; /* struct. to hold stat(2) o/p contents */ -struct passwd *user1; /* struct. to hold getpwnam(3) o/p contents */ +static uid_t nobody_uid; +static gid_t nobody_gid, free_gid; -char *TCID = "mknod05"; -int TST_TOTAL = 1; -char node_name[PATH_MAX]; /* buffer to hold node name created */ - -gid_t group1_gid, group2_gid, mygid; /* user and process group id's */ -uid_t save_myuid, user1_uid; /* user and process user id's */ -pid_t mypid; /* process id */ - -void setup(); /* setup function for the test */ -void cleanup(); /* cleanup function for the test */ - -int main(int ac, char **av) +static void run(void) { - int lc; - int fflag; + struct stat buf; - tst_parse_opts(ac, av, NULL, NULL); + SAFE_MKNOD(TEMP_NODE, MODE_FIFO_SGID, 0); - setup(); + SAFE_STAT(TEMP_NODE, &buf); + TST_EXP_EQ_LI(buf.st_mode & S_ISGID, S_ISGID); + TST_EXP_EQ_LI(buf.st_gid, free_gid); - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Attempt to create a filesystem node with group id (sgid) - * bit set on a directory with group id (sgid) bit set - * such that, the node created by mknod(2) should have - * group id (sgid) bit set and node's gid should be equal - * to that of effective gid of the process. - */ - TEST(mknod(node_name, MODE_SGID, 0)); - - /* Check return code from mknod(2) */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "mknod(%s, %#o, 0) failed, errno=%d : " - "%s", node_name, MODE_SGID, TEST_ERRNO, - strerror(TEST_ERRNO)); - continue; - } - /* Set the functionality flag */ - fflag = 1; - - /* Check for node's creation */ - if (stat(node_name, &buf) < 0) { - tst_resm(TFAIL, "stat() of %s failed, errno:%d", - node_name, TEST_ERRNO); - /* unset functionality flag */ - fflag = 0; - } - - /* Verify mode permissions of node */ - if (!(buf.st_mode & S_ISGID)) { - tst_resm(TFAIL, "%s: Incorrect modes, " - "setgid bit not set", node_name); - /* unset flag as functionality fails */ - fflag = 0; - } - - /* Verify group ID */ - if (buf.st_gid != group2_gid) { - tst_resm(TFAIL, "%s: Incorrect group", - node_name); - /* unset flag as functionality fails */ - fflag = 0; - } - if (fflag) { - tst_resm(TPASS, "Functionality of mknod(%s, " - "%#o, 0) successful", - node_name, MODE_SGID); - } - - /* Remove the node for the next go `round */ - if (unlink(node_name) == -1) { - tst_resm(TWARN, "unlink(%s) failed, errno:%d %s", - node_name, errno, strerror(errno)); - } - } - - /* change the directory back to temporary directory */ - SAFE_CHDIR(cleanup, ".."); - - /* - * Invoke cleanup() to delete the test directories created - * in the setup() and exit main(). - */ - cleanup(); - - tst_exit(); + SAFE_UNLINK(TEMP_NODE); } -/* - * setup(void) - performs all ONE TIME setup for this test. - * Exit the test program on receipt of unexpected signals. - * Create a temporary directory used to hold test directories created - * and change the directory to it. - * Verify that pid of process executing the test is root. - * Create a test directory on temporary directory and set the ownership - * of test directory to guest user and process, change mode permissions - * to set group id bit on it. - */ -void setup(void) +static void setup(void) { - tst_require_root(); + struct passwd *ltpuser = SAFE_GETPWNAM("nobody"); - /* Capture unexpected signals */ - tst_sig(NOFORK, DEF_HANDLER, cleanup); + nobody_uid = ltpuser->pw_uid; + nobody_gid = ltpuser->pw_gid; + free_gid = tst_get_free_gid(nobody_gid); - TEST_PAUSE; - - /* Make a temp dir and cd to it */ - tst_tmpdir(); - - /* Save the real user id of the current test process */ - save_myuid = getuid(); - - /* Save the process id of the current test process */ - mypid = getpid(); - - /* Get the node name to be created in the test */ - sprintf(node_name, TNODE, mypid); - - /* Get the uid/gid of ltp user */ - if ((user1 = getpwnam(LTPUSER)) == NULL) { - tst_brkm(TBROK, cleanup, "%s not in /etc/passwd", LTPUSER); - } - user1_uid = user1->pw_uid; - group1_gid = user1->pw_gid; - - /* Get the effective group id of the test process */ - group2_gid = getegid(); - - /* - * Create a test directory under temporary directory with the - * specified mode permissions, with uid/gid set to that of guest - * user and the test process. - */ - SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX); - SAFE_CHOWN(cleanup, DIR_TEMP, user1_uid, group2_gid); - SAFE_CHMOD(cleanup, DIR_TEMP, MODE_SGID); - - /* - * Verify that test directory created with expected permission modes - * and ownerships. - */ - SAFE_STAT(cleanup, DIR_TEMP, &buf); - /* Verify modes of test directory */ - if (!(buf.st_mode & S_ISGID)) { - tst_brkm(TBROK, cleanup, - "%s: Incorrect modes, setgid bit not set", DIR_TEMP); - } - - /* Verify group ID of test directory */ - if (buf.st_gid != group2_gid) { - tst_brkm(TBROK, cleanup, "%s: Incorrect group", DIR_TEMP); - } - - /* Change directory to DIR_TEMP */ - SAFE_CHDIR(cleanup, DIR_TEMP); + SAFE_MKDIR(TEMP_DIR, MODE_RWX); + SAFE_CHOWN(TEMP_DIR, nobody_uid, free_gid); + SAFE_CHMOD(TEMP_DIR, MODE_FIFO_SGID); } -/* - * cleanup() - Performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Print test timing stats and errno log if test executed with options. - * Remove temporary directory and sub-directories/files under it - * created during setup(). - * Exit the test program with normal exit code. - */ -void cleanup(void) -{ - - tst_rmdir(); - -} +static struct tst_test test = { + .setup = setup, + .test_all = run, + .needs_root = 1, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/mknod/mknod06.c b/testcases/kernel/syscalls/mknod/mknod06.c index 8f70cf07..a7b16111 100755 --- a/testcases/kernel/syscalls/mknod/mknod06.c +++ b/testcases/kernel/syscalls/mknod/mknod06.c @@ -1,280 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: mknod06 - * - * Test Description: - * Verify that, - * 1) mknod(2) returns -1 and sets errno to EEXIST if specified path - * already exists. - * 2) mknod(2) returns -1 and sets errno to EFAULT if pathname points - * outside user's accessible address space. - * 3) mknod(2) returns -1 and sets errno to ENOENT if the directory - * component in pathname does not exist. - * 4) mknod(2) returns -1 and sets errno to ENAMETOOLONG if the pathname - * component was too long. - * 5) mknod(2) returns -1 and sets errno to ENOTDIR if the directory - * component in pathname is not a directory. - * - * Expected Result: - * mknod() should fail with return value -1 and set expected errno. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * if errno set == expected errno - * Issue sys call fails with expected return value and errno. - * Otherwise, - * Issue sys call fails with unexpected errno. - * Otherwise, - * Issue sys call returns unexpected value. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory(s)/file(s) created. - * - * Usage: - * mknod06 [-c n] [-e] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * This test should be executed by super-user (root) only. + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +/*\ + * Verify that mknod(2) fails with the correct error codes: + * + * - ENAMETOOLONG if the pathname component was too long. + * - EEXIST if specified path already exists. + * - EFAULT if pathname points outside user's accessible address space. + * - ENOENT if the directory component in pathname does not exist. + * - ENOENT if the pathname is empty. + * - ENOTDIR if the directory component in pathname is not a directory. + */ -#include "test.h" +#include "tst_test.h" -#define MODE_RWX S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO +#define MODE_FIFO_RWX (S_IFIFO | 0777) -int setup1(); /* setup function to test mknod for EEXIST */ -int setup3(); /* setup function to test mknod for ENOTDIR */ -int longpath_setup(); /* setup function to test mknod for ENAMETOOLONG */ -int no_setup(); /* simply returns 0 to the caller */ -char Longpathname[PATH_MAX + 2]; +static char *longpathname; -struct test_case_t { /* test case struct. to hold ref. test cond's */ +static struct tcase { char *pathname; char *desc; int exp_errno; - int (*setupfunc) (); -} Test_cases[] = { - {"tnode_1", "Specified node already exists", EEXIST, setup1}, { - NULL, "Invalid address", EFAULT, no_setup}, { - "testdir_2/tnode_2", "Non-existent file", ENOENT, no_setup}, { - "", "Pathname is empty", ENOENT, no_setup}, { - Longpathname, "Pathname too long", ENAMETOOLONG, longpath_setup}, { - "tnode/tnode_3", "Path contains regular file", ENOTDIR, setup3}, { - NULL, NULL, 0, no_setup} +} tcases[] = { + { NULL, "Pathname too long", ENAMETOOLONG }, + { "tnode_1", "Specified node already exists", EEXIST }, + { NULL, "Invalid address", EFAULT }, + { "testdir_2/tnode_2", "Non-existent file", ENOENT }, + { "", "Pathname is empty", ENOENT }, + { "tnode/tnode_3", "Path contains regular file", ENOTDIR }, }; -char *TCID = "mknod06"; -int TST_TOTAL = ARRAY_SIZE(Test_cases); - -void setup(); /* setup function for the tests */ -void cleanup(); /* cleanup function for the tests */ - -int main(int ac, char **av) +static void run(unsigned int i) { - int lc; - char *node_name; /* ptr. for node name created */ - char *test_desc; /* test specific error message */ - int ind; /* counter to test different test conditions */ + struct tcase *tc = &tcases[i]; - tst_parse_opts(ac, av, NULL, NULL); - - /* - * Invoke setup function to call individual test setup functions - * for the test which run as root/super-user. - */ - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - for (ind = 0; Test_cases[ind].desc != NULL; ind++) { - node_name = Test_cases[ind].pathname; - test_desc = Test_cases[ind].desc; - - /* - * Call mknod(2) to test different test conditions. - * verify that it fails with -1 return value and - * sets appropriate errno. - */ - TEST(mknod(node_name, MODE_RWX, 0)); - - /* Check return code from mknod(2) */ - if (TEST_RETURN != -1) { - tst_resm(TFAIL, - "mknod() returned %ld, expected " - "-1, errno:%d", TEST_RETURN, - Test_cases[ind].exp_errno); - continue; - } - - if (TEST_ERRNO == Test_cases[ind].exp_errno) { - tst_resm(TPASS, "mknod() fails, %s, errno:%d", - test_desc, TEST_ERRNO); - } else { - tst_resm(TFAIL, "mknod() fails, %s, errno:%d, " - "expected errno:%d", test_desc, - TEST_ERRNO, Test_cases[ind].exp_errno); - } - } - - } - - /* - * Invoke cleanup() to delete the test directories created - * in the setup(). - */ - cleanup(); - - tst_exit(); + TST_EXP_FAIL(mknod(tc->pathname, MODE_FIFO_RWX, 0), tc->exp_errno, "%s", + tc->desc); } -/* - * setup(void) - performs all ONE TIME setup for this test. - * Exit the test program on receipt of unexpected signals. - * Create a temporary directory used to hold test directories and nodes - * created and change the directory to it. - * Invoke individual test setup functions according to the order - * set in struct. definition. - */ -void setup(void) +static void setup(void) { - int ind; + SAFE_MKNOD("tnode_1", MODE_FIFO_RWX, 0); + SAFE_MKNOD("tnode", MODE_FIFO_RWX, 0); - tst_require_root(); - - /* Capture unexpected signals */ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - /* Make a temp dir and cd to it */ - tst_tmpdir(); - - /* call individual setup functions */ - for (ind = 0; Test_cases[ind].desc != NULL; ind++) { - if (!Test_cases[ind].pathname) - Test_cases[ind].pathname = tst_get_bad_addr(cleanup); - Test_cases[ind].setupfunc(); - } + for (int i = 0; i <= (PATH_MAX + 1); i++) + longpathname[i] = 'a'; + tcases[0].pathname = longpathname; } -/* - * no_setup() - Some test conditions for mknod(2) do not any setup. - * Hence, this function just returns 0. - */ -int no_setup(void) -{ - return 0; -} - -/* - * longpath_setup() - setup to create a node with a name length exceeding - * the MAX. length of PATH_MAX. - * This function retruns 0. - */ -int longpath_setup(void) -{ - int ind; /* counter variable */ - - for (ind = 0; ind <= (PATH_MAX + 1); ind++) { - Longpathname[ind] = 'a'; - } - return 0; -} - -/* - * setup1() - setup function for a test condition for which mknod(2) - * returns -1 and sets errno to EEXIST. - * This function creates a node using mknod(2) and tries to create - * same node in the test and fails with above errno. - * This function returns 0. - */ -int setup1(void) -{ - /* Create a node using mknod */ - if (mknod("tnode_1", MODE_RWX, 0) < 0) { - tst_brkm(TBROK, cleanup, "Fails to create node in setup1()"); - } - - return 0; -} - -/* - * setup3() - setup function for a test condition for which mknod(2) - * returns -1 and sets errno to ENOTDIR. - * This function creates a node under temporary directory and the - * test attempts to create another node under under this node and fails - * with ENOTDIR as the node created here is a regular file. - * This function returns 0. - */ -int setup3(void) -{ - /* Create a node using mknod */ - if (mknod("tnode", MODE_RWX, 0) < 0) { - tst_brkm(TBROK, cleanup, "Fails to create node in setup3()"); - } - - return 0; -} - -/* - * cleanup() - Performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Print test timing stats and errno log if test executed with options. - * Remove temporary directory and sub-directories/files under it - * created during setup(). - * Exit the test program with normal exit code. - */ -void cleanup(void) -{ - - tst_rmdir(); - -} +static struct tst_test test = { + .setup = setup, + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .needs_tmpdir = 1, + .bufs = (struct tst_buffers[]) { + { &longpathname, .size = PATH_MAX + 2 }, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/mknod/mknod07.c b/testcases/kernel/syscalls/mknod/mknod07.c index 82919906..76701a34 100755 --- a/testcases/kernel/syscalls/mknod/mknod07.c +++ b/testcases/kernel/syscalls/mknod/mknod07.c @@ -1,184 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * 07/2001 Ported by Wayne Boyer - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -/* - * - * Test Description: - * Verify that, - * 1) mknod(2) returns -1 and sets errno to EPERM if the process id of - * the caller is not super-user. - * 2) mknod(2) returns -1 and sets errno to EACCES if parent directory - * does not allow write permission to the process. - * 3) mknod(2) returns -1 and sets errno to EROFS if pathname refers to - * a file on a read-only file system. - * 4) mknod(2) returns -1 and sets errno to ELOOP if too many symbolic - * links were encountered in resolving pathname. +/*\ + * Verify that mknod(2) fails with the correct error codes: * + * - EACCES if parent directory does not allow write permission to the process. + * - EPERM if the process id of the caller is not super-user. + * - EROFS if pathname refers to a file on a read-only file system. + * - ELOOP if too many symbolic links were encountered in resolving pathname. */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" -#define DIR_TEMP "testdir_1" -#define DIR_TEMP_MODE (S_IRUSR | S_IXUSR) -#define DIR_MODE (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP| \ - S_IXGRP|S_IROTH|S_IXOTH) -#define MNT_POINT "mntpoint" +#define TEMP_MNT "mnt" +#define TEMP_DIR "testdir" +#define TEMP_DIR_MODE 0500 -#define FIFO_MODE (S_IFIFO | S_IRUSR | S_IRGRP | S_IROTH) -#define SOCKET_MODE (S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO) -#define CHR_MODE (S_IFCHR | S_IRUSR | S_IWUSR) -#define BLK_MODE (S_IFBLK | S_IRUSR | S_IWUSR) +#define ELOOP_DIR "test_eloop" +#define ELOOP_FILE "/test_eloop" +#define ELOOP_DIR_MODE 0755 +#define ELOOP_MAX 43 -#define ELOPFILE "/test_eloop" +#define FIFO_MODE (S_IFIFO | 0444) +#define SOCKET_MODE (S_IFSOCK | 0777) +#define CHR_MODE (S_IFCHR | 0600) +#define BLK_MODE (S_IFBLK | 0600) -static char elooppathname[sizeof(ELOPFILE) * 43] = "."; +static char *elooppathname; -static const char *device; -static int mount_flag; - -static struct test_case_t { +static struct tcase { char *pathname; int mode; int exp_errno; int major, minor; -} test_cases[] = { - { "testdir_1/tnode_1", SOCKET_MODE, EACCES, 0, 0 }, - { "testdir_1/tnode_2", FIFO_MODE, EACCES, 0, 0 }, - { "tnode_3", CHR_MODE, EPERM, 1, 3 }, - { "tnode_4", BLK_MODE, EPERM, 0, 0 }, - { "mntpoint/tnode_5", SOCKET_MODE, EROFS, 0, 0 }, - { elooppathname, FIFO_MODE, ELOOP, 0, 0 }, +} tcases[] = { + { NULL, FIFO_MODE, ELOOP, 0, 0 }, + { TEMP_DIR "/tnode1", SOCKET_MODE, EACCES, 0, 0 }, + { TEMP_DIR "/tnode2", FIFO_MODE, EACCES, 0, 0 }, + { "tnode3", CHR_MODE, EPERM, 1, 3 }, + { "tnode4", BLK_MODE, EPERM, 0, 0 }, + { TEMP_MNT "/tnode5", SOCKET_MODE, EROFS, 0, 0 }, }; -char *TCID = "mknod07"; -int TST_TOTAL = ARRAY_SIZE(test_cases); +#define TEST_SIZE ARRAY_SIZE(tcases) -static void setup(void); -static void mknod_verify(const struct test_case_t *test_case); -static void cleanup(void); - -int main(int ac, char **av) +static void run(unsigned int i) { - int lc; - int i; + struct tcase *tc = &tcases[i]; - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; i < TST_TOTAL; i++) - mknod_verify(&test_cases[i]); - } - - cleanup(); - tst_exit(); + TST_EXP_FAIL(mknod(tc->pathname, tc->mode, + makedev(tc->major, tc->minor)), + tc->exp_errno); } static void setup(void) { - int i; - struct passwd *ltpuser; - const char *fs_type; + struct passwd *ltpuser = SAFE_GETPWNAM("nobody"); - tst_require_root(); + SAFE_SETEUID(ltpuser->pw_uid); + SAFE_MKDIR(TEMP_DIR, TEMP_DIR_MODE); - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - tst_tmpdir(); - - fs_type = tst_dev_fs_type(); - device = tst_acquire_device(cleanup); - - if (!device) - tst_brkm(TCONF, cleanup, "Failed to acquire device"); - - tst_mkfs(cleanup, device, fs_type, NULL, NULL); - - TEST_PAUSE; - - /* mount a read-only file system for EROFS test */ - SAFE_MKDIR(cleanup, MNT_POINT, DIR_MODE); - SAFE_MOUNT(cleanup, device, MNT_POINT, fs_type, MS_RDONLY, NULL); - mount_flag = 1; - - ltpuser = SAFE_GETPWNAM(cleanup, "nobody"); - SAFE_SETEUID(cleanup, ltpuser->pw_uid); - - SAFE_MKDIR(cleanup, DIR_TEMP, DIR_TEMP_MODE); + SAFE_MKDIR(ELOOP_DIR, ELOOP_DIR_MODE); + SAFE_SYMLINK("../test_eloop", "test_eloop/test_eloop"); /* - * NOTE: the ELOOP test is written based on that the consecutive - * symlinks limits in kernel is hardwired to 40. + * The kernel limits symlink resolution hop amount to 40, + * create a pathname with more than that */ - SAFE_MKDIR(cleanup, "test_eloop", DIR_MODE); - SAFE_SYMLINK(cleanup, "../test_eloop", "test_eloop/test_eloop"); - for (i = 0; i < 43; i++) - strcat(elooppathname, ELOPFILE); + strcpy(elooppathname, "."); + for (int i = 0; i < ELOOP_MAX; i++) + strcat(elooppathname, ELOOP_FILE); + tcases[0].pathname = elooppathname; } -static void mknod_verify(const struct test_case_t *test_case) -{ - TEST(mknod(test_case->pathname, test_case->mode, - makedev(test_case->major, test_case->minor))); - - if (TEST_RETURN != -1) { - tst_resm(TFAIL, "mknod succeeded unexpectedly"); - return; - } - - if (TEST_ERRNO == test_case->exp_errno) { - tst_resm(TPASS | TTERRNO, "mknod failed as expected"); - } else { - tst_resm(TFAIL | TTERRNO, - "mknod failed unexpectedly; expected: " - "%d - %s", test_case->exp_errno, - strerror(test_case->exp_errno)); - } -} - -static void cleanup(void) -{ - if (seteuid(0) == -1) - tst_resm(TWARN | TERRNO, "seteuid(0) failed"); - - if (mount_flag && tst_umount(MNT_POINT) < 0) - tst_resm(TWARN | TERRNO, "umount device:%s failed", device); - - if (device) - tst_release_device(device); - - tst_rmdir(); -} +static struct tst_test test = { + .setup = setup, + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .mntpoint = TEMP_MNT, + .needs_rofs = 1, + .bufs = (struct tst_buffers[]){ + { &elooppathname, .size = sizeof(ELOOP_FILE) * ELOOP_MAX }, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/mknod/mknod08.c b/testcases/kernel/syscalls/mknod/mknod08.c index 8647fdeb..75810e57 100755 --- a/testcases/kernel/syscalls/mknod/mknod08.c +++ b/testcases/kernel/syscalls/mknod/mknod08.c @@ -1,296 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: mknod08 - * - * Test Description: - * Verify that mknod(2) succeeds when used to create a filesystem - * node on a directory without set group-ID bit set. The node created - * should not have set group-ID bit set and its gid should be equal to that - * of its parent directory. - * - * Expected Result: - * mknod() should return value 0 on success and node created should not - * have set group-ID bit set. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * mknod08 [-c n] [-e] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * This test should be run by 'super-user' (root) only. - * + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that mknod(2) succeeds when used to create a filesystem node on a + * directory without set group-ID bit set. The node created should not have + * set group-ID bit set and its gid should be equal to that of its parent + * directory. */ -#include -#include -#include -#include -#include -#include #include -#include -#include +#include "tst_uid.h" +#include "tst_test.h" -#include "test.h" -#include "safe_macros.h" +#define MODE_RWX 0777 +#define MODE_FIFO_RWX (S_IFIFO | 0777) -#define LTPUSER "nobody" -#define MODE_RWX S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO -#define DIR_TEMP "testdir_1" -#define TNODE "tnode_%d" +#define TEMP_DIR "tempdir" +#define TEMP_NODE TEMP_DIR "/testnode" -struct stat buf; /* struct. to hold stat(2) o/p contents */ -struct passwd *user1; /* struct. to hold getpwnam(3) o/p contents */ +static uid_t nobody_uid; +static gid_t nobody_gid, free_gid; -char *TCID = "mknod08"; -int TST_TOTAL = 1; -char node_name[PATH_MAX]; /* buffer to hold node name created */ - -gid_t group1_gid, group2_gid, mygid; /* user and process group id's */ -uid_t save_myuid, user1_uid; /* user and process user id's */ -pid_t mypid; /* process id */ - -void setup(); /* setup function for the test */ -void cleanup(); /* cleanup function for the test */ - -int main(int ac, char **av) +static void run(void) { - int lc; - int fflag; + struct stat buf; - tst_parse_opts(ac, av, NULL, NULL); + SAFE_MKNOD(TEMP_NODE, MODE_FIFO_RWX, 0); - setup(); + SAFE_STAT(TEMP_NODE, &buf); + TST_EXP_EQ_LI(buf.st_mode & S_ISGID, 0); + TST_EXP_EQ_LI(buf.st_gid, nobody_gid); - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Call mknod() to creat a node on a directory without - * set group-ID (sgid) bit set. - */ - TEST(mknod(node_name, MODE_RWX, 0)); - - /* Check return code from mknod(2) */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL, - "mknod(%s, %#o, 0) failed, errno=%d : %s", - node_name, MODE_RWX, TEST_ERRNO, - strerror(TEST_ERRNO)); - continue; - } - /* Set the functionality flag */ - fflag = 1; - - /* Check for node's creation */ - if (stat(node_name, &buf) < 0) { - tst_resm(TFAIL, - "stat() of %s failed, errno:%d", - node_name, TEST_ERRNO); - /* unset flag as functionality fails */ - fflag = 0; - } - - /* Verify mode permissions of node */ - if (buf.st_mode & S_ISGID) { - tst_resm(TFAIL, "%s: Incorrect modes, setgid " - "bit set", node_name); - /* unset flag as functionality fails */ - fflag = 0; - } - - /* Verify group ID */ - if (buf.st_gid != mygid) { - tst_resm(TFAIL, "%s: Incorrect group", - node_name); - /* unset flag as functionality fails */ - fflag = 0; - } - if (fflag) { - tst_resm(TPASS, "Functionality of mknod(%s, " - "%#o, 0) successful", - node_name, MODE_RWX); - } - - /* Remove the node for the next go `round */ - if (unlink(node_name) == -1) { - tst_resm(TWARN, - "unlink(%s) failed, errno:%d %s", - node_name, errno, strerror(errno)); - } - } - - /* Change the directory back to temporary directory */ - SAFE_CHDIR(cleanup, ".."); - - /* - * Invoke cleanup() to delete the test directories created - * in the setup() and exit main(). - */ - cleanup(); - - tst_exit(); + SAFE_UNLINK(TEMP_NODE); } -/* - * setup(void) - performs all ONE TIME setup for this test. - * Exit the test program on receipt of unexpected signals. - * Create a temporary directory used to hold test directories created - * and change the directory to it. - * Verify that pid of process executing the test is root. - * Create a test directory on temporary directory and set the ownership - * of test directory to nobody user. - * Set the effective uid/gid of the process to that of nobody user. - */ -void setup(void) +static void setup(void) { - tst_require_root(); + struct passwd *ltpuser = SAFE_GETPWNAM("nobody"); - /* Capture unexpected signals */ - tst_sig(NOFORK, DEF_HANDLER, cleanup); + nobody_uid = ltpuser->pw_uid; + nobody_gid = ltpuser->pw_gid; + free_gid = tst_get_free_gid(nobody_gid); - TEST_PAUSE; - - /* Make a temp dir and cd to it */ - tst_tmpdir(); - - /* fix permissions on the tmpdir */ - if (chmod(".", 0711) != 0) { - tst_brkm(TBROK, cleanup, "chmod() failed"); - } - - /* Save the real user id of the test process */ - save_myuid = getuid(); - - /* Save the process id of the test process */ - mypid = getpid(); - - /* Get the node name to be created in the test */ - sprintf(node_name, TNODE, mypid); - - /* Get the uid/gid of guest user - nobody */ - if ((user1 = getpwnam(LTPUSER)) == NULL) { - tst_brkm(TBROK, cleanup, "%s not in /etc/passwd", LTPUSER); - } - user1_uid = user1->pw_uid; - group1_gid = user1->pw_gid; - - /* Get effective group id of the test process */ - group2_gid = getegid(); - - /* - * Create a test directory under temporary directory with the - * specified mode permissions, with uid/gid set to that of guest - * user and the test process. - */ - SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX); - SAFE_CHOWN(cleanup, DIR_TEMP, user1_uid, group2_gid); - - /* - * Verify that test directory created with expected permission modes - * and ownerships. - */ - SAFE_STAT(cleanup, DIR_TEMP, &buf); - - /* Verify modes of test directory */ - if (buf.st_mode & S_ISGID) { - tst_brkm(TBROK, cleanup, - "%s: Incorrect modes, setgid bit set", DIR_TEMP); - } - - /* Verify group ID */ - if (buf.st_gid != group2_gid) { - tst_brkm(TBROK, cleanup, "%s: Incorrect group", DIR_TEMP); - } - - /* - * Set the effective group id and user id of the test process - * to that of guest user. - */ - SAFE_SETGID(cleanup, group1_gid); - if (setreuid(-1, user1_uid) < 0) { - tst_brkm(TBROK, cleanup, - "Unable to set process uid to that of ltp user"); - } - - /* Save the real group ID of the current process */ - mygid = getgid(); - - /* Change directory to DIR_TEMP */ - SAFE_CHDIR(cleanup, DIR_TEMP); + SAFE_MKDIR(TEMP_DIR, MODE_RWX); + SAFE_CHOWN(TEMP_DIR, nobody_uid, free_gid); + SAFE_SETGID(nobody_gid); } -/* - * cleanup() - Performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Print test timing stats and errno log if test executed with options. - * Restore the real/effective user id of the process changed during - * setup(). - * Remove temporary directory and sub-directories/files under it - * created during setup(). - * Exit the test program with normal exit code. - */ -void cleanup(void) -{ - - /* - * Restore the effective uid of the process changed in the - * setup(). - */ - if (setreuid(-1, save_myuid) < 0) { - tst_brkm(TBROK, NULL, - "resetting process real/effective uid failed"); - } - - tst_rmdir(); - -} +static struct tst_test test = { + .setup = setup, + .test_all = run, + .needs_root = 1, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/mknod/mknod09.c b/testcases/kernel/syscalls/mknod/mknod09.c index 57b95379..bb6e58a9 100755 --- a/testcases/kernel/syscalls/mknod/mknod09.c +++ b/testcases/kernel/syscalls/mknod/mknod09.c @@ -1,149 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* - * Copyright (C) Bull S.A. 2001 - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -/* - * Test Name: mknod09 - * - * Test Description: - * Verify that, mknod() fails with -1 and sets errno to EINVAL if the mode is - * different than a normal file, device special file or FIFO. - * - * Expected Result: - * mknod() should fail with return value -1 and sets expected errno. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Check id is super/root - * Pause for SIGUSR1 if option specified. - * Create temporary directory. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * if errno set == expected errno - * Issue sys call fails with expected return value and errno. - * Otherwise, - * Issue sys call fails with unexpected errno. - * Otherwise, - * Issue sys call returns unexpected value. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * mknod09 [-c n] [-e] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 05/2002 Ported by André Merlier - * - * RESTRICTIONS: - * This test should be run by 'super-user' (root) only. - * + * Copyright (C) Bull S.A. 2001 + * Copyright (c) International Business Machines Corp., 2001 + * 05/2002 Ported by André Merlier + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -#include -#include -#include "test.h" +/*\ + * Verify that mknod() fails with -1 and sets errno to EINVAL if the mode is + * different than a normal file, device special file or FIFO. + */ -#define MODE_RWX S_IFMT /* mode different from those expected */ -#define TNODE "tnode" /*pathname */ +#include "tst_test.h" -char *TCID = "mknod09"; -int TST_TOTAL = 1; - -void setup(); /* setup function for the test */ -void cleanup(); /* cleanup function for the test */ - -int main(int ac, char **av) +static void check_mknod(void) { - int lc; - char *test_desc; /* test specific error message */ - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - test_desc = "EINVAL"; - - tst_count = 0; - - /* - * Call mknod(2) to test condition. - * verify that it fails with -1 return value and - * sets appropriate errno. - */ - TEST(mknod(TNODE, MODE_RWX, 0)); - - /* Check return code from mknod(2) */ - if (TEST_RETURN != -1) { - tst_resm(TFAIL, "mknod() returned %ld, " - "expected -1, errno=%d", TEST_RETURN, - EINVAL); - } else { - if (TEST_ERRNO == EINVAL) { - tst_resm(TPASS, "mknod() fails with expected " - "error EINVAL errno:%d", TEST_ERRNO); - } else { - tst_resm(TFAIL, "mknod() fails, %s, " - "errno=%d, expected errno=%d", - test_desc, TEST_ERRNO, EINVAL); - } - } - } - - cleanup(); - - tst_exit(); + TST_EXP_FAIL(mknod("tnode", S_IFMT, 0), EINVAL); } -/* - * setup(void) - */ -void setup(void) -{ - tst_require_root(); - - /* Capture unexpected signals */ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - /* Make a temp dir and cd to it */ - tst_tmpdir(); -} - -/* - * cleanup() - */ -void cleanup(void) -{ - - tst_rmdir(); - -} +static struct tst_test test = { + .test_all = check_mknod, + .needs_tmpdir = 1, + .needs_root = 1 +}; diff --git a/testcases/kernel/syscalls/mknodat/mknodat.h b/testcases/kernel/syscalls/mknodat/mknodat.h deleted file mode 100755 index 8f3a1f00..00000000 --- a/testcases/kernel/syscalls/mknodat/mknodat.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2007 - * Copyright (c) 2014 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 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 Library 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. - * - */ - -#ifndef MKNODAT_H -#define MKNODAT_H - -#include -#include "config.h" -#include "lapi/syscalls.h" - -#if !defined(HAVE_MKNODAT) -int mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev) -{ - return tst_syscall(__NR_mknodat, dirfd, filename, mode, dev); -} -#endif - -#endif /* MKNODAT_H */ diff --git a/testcases/kernel/syscalls/mknodat/mknodat01.c b/testcases/kernel/syscalls/mknodat/mknodat01.c index 6500ca36..3be0a4f8 100755 --- a/testcases/kernel/syscalls/mknodat/mknodat01.c +++ b/testcases/kernel/syscalls/mknodat/mknodat01.c @@ -35,7 +35,6 @@ #include "test.h" #include "safe_macros.h" #include "lapi/fcntl.h" -#include "mknodat.h" #define PATHNAME "mknodattestdir" diff --git a/testcases/kernel/syscalls/mknodat/mknodat02.c b/testcases/kernel/syscalls/mknodat/mknodat02.c index eda247fd..fdac5db1 100755 --- a/testcases/kernel/syscalls/mknodat/mknodat02.c +++ b/testcases/kernel/syscalls/mknodat/mknodat02.c @@ -37,7 +37,7 @@ #include "test.h" #include "safe_macros.h" #include "lapi/fcntl.h" -#include "mknodat.h" +#include "lapi/syscalls.h" static void setup(void); static void cleanup(void); diff --git a/testcases/kernel/syscalls/mlock/.gitignore b/testcases/kernel/syscalls/mlock/.gitignore index 306574bb..1872229b 100755 --- a/testcases/kernel/syscalls/mlock/.gitignore +++ b/testcases/kernel/syscalls/mlock/.gitignore @@ -2,3 +2,4 @@ /mlock02 /mlock03 /mlock04 +/mlock05 diff --git a/testcases/kernel/syscalls/mlock/mlock01.c b/testcases/kernel/syscalls/mlock/mlock01.c index 0b079f8b..007cffa2 100755 --- a/testcases/kernel/syscalls/mlock/mlock01.c +++ b/testcases/kernel/syscalls/mlock/mlock01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test mlock with various valid addresses and lengths. */ diff --git a/testcases/kernel/syscalls/mlock/mlock02.c b/testcases/kernel/syscalls/mlock/mlock02.c index 921ddeea..799a2276 100755 --- a/testcases/kernel/syscalls/mlock/mlock02.c +++ b/testcases/kernel/syscalls/mlock/mlock02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test for ENOMEM, EPERM errors. * * 1) mlock(2) fails with ENOMEM if some of the specified address range diff --git a/testcases/kernel/syscalls/mlock/mlock03.c b/testcases/kernel/syscalls/mlock/mlock03.c index 3700b64b..0ac5112d 100755 --- a/testcases/kernel/syscalls/mlock/mlock03.c +++ b/testcases/kernel/syscalls/mlock/mlock03.c @@ -1,11 +1,9 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010 Red Hat, Inc. */ /*\ - * [Description] - * * This case is a regression test on old RHEL5. * * Stack size mapping is decreased through mlock/munlock call. @@ -31,18 +29,16 @@ #include "tst_test.h" #include "tst_safe_stdio.h" -#define KB 1024 - static void verify_mlock(void) { long from, to; long first = -1, last = -1; - char b[KB]; + char b[TST_KB]; FILE *fp; fp = SAFE_FOPEN("/proc/self/maps", "r"); while (!feof(fp)) { - if (!fgets(b, KB - 1, fp)) + if (!fgets(b, TST_KB - 1, fp)) break; b[strlen(b) - 1] = '\0'; if (sscanf(b, "%lx-%lx", &from, &to) != 2) { @@ -53,7 +49,7 @@ static void verify_mlock(void) /* Record the initial stack size. */ if (strstr(b, "[stack]") != NULL) - first = (to - from) / KB; + first = (to - from) / TST_KB; tst_res(TINFO, "mlock [%lx,%lx]", from, to); if (mlock((const void *)from, to - from) == -1) @@ -65,7 +61,7 @@ static void verify_mlock(void) /* Record the final stack size. */ if (strstr(b, "[stack]") != NULL) - last = (to - from) / KB; + last = (to - from) / TST_KB; } SAFE_FCLOSE(fp); diff --git a/testcases/kernel/syscalls/mlock/mlock04.c b/testcases/kernel/syscalls/mlock/mlock04.c index f25460ba..2a65742b 100755 --- a/testcases/kernel/syscalls/mlock/mlock04.c +++ b/testcases/kernel/syscalls/mlock/mlock04.c @@ -1,16 +1,11 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010 Red Hat, Inc. */ /*\ - * [Description] - * * This is a reproducer copied from one of LKML patch submission - * which subject is - * - * [PATCH] mlock: revert the optimization for dirtying pages and triggering writeback. - * url see https://www.spinics.net/lists/kernel/msg1141090.html + * https://lore.kernel.org/lkml/1296371720-4176-1-git-send-email-tm@tao.ma/ * * "In 5ecfda0, we do some optimization in mlock, but it causes * a very basic test case(attached below) of mlock to fail. So diff --git a/testcases/kernel/syscalls/mlock/mlock05.c b/testcases/kernel/syscalls/mlock/mlock05.c new file mode 100644 index 00000000..4e432fc8 --- /dev/null +++ b/testcases/kernel/syscalls/mlock/mlock05.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright Red Hat + * Author: Filippo Storniolo + */ + +/*\ + * Verify mlock() causes pre-faulting of PTEs and prevent memory to be swapped out. + * + * Find the new mapping in /proc/$pid/smaps and check Rss and Locked fields after + * mlock syscall: + * Rss and Locked size should be equal to the size of the memory allocation + */ + +#include "tst_test.h" +#include "tst_safe_stdio.h" + +#define MMAPLEN (1UL<<20) +#define LINELEN 256 + +static void get_proc_smaps_info(unsigned long desired_mapping_address, unsigned long *Rss, unsigned long *Locked) +{ + bool mapping_found = false; + bool Locked_found = false; + bool Rss_found = false; + char buffer[LINELEN]; + FILE *fp; + int ret; + + fp = SAFE_FOPEN("/proc/self/smaps", "r"); + + while (fgets(buffer, LINELEN, fp) != NULL) { + unsigned long mapping_address; + + ret = sscanf(buffer, "%lx[^-]", &mapping_address); + if ((ret == 1) && (mapping_address == desired_mapping_address)) { + mapping_found = true; + break; + } + } + + if (!mapping_found) { + SAFE_FCLOSE(fp); + tst_brk(TBROK, "Mapping %lx not found in /proc/self/smaps", desired_mapping_address); + return; + } + + while (fgets(buffer, LINELEN, fp) != NULL) { + unsigned long possible_starting_mapping; + unsigned long possible_ending_mapping; + + ret = sscanf(buffer, "%lx-%lx", &possible_starting_mapping, &possible_ending_mapping); + if (ret == 2) + break; + + if (strncmp(buffer, "Rss", strlen("Rss")) == 0) { + ret = sscanf(buffer, "%*[^:]:%lu kB", Rss); + if (ret != 1) { + SAFE_FCLOSE(fp); + tst_brk(TBROK, "failure occurred while reading field Rss"); + return; + } + + Rss_found = true; + } + + if (strncmp(buffer, "Locked", strlen("Locked")) == 0) { + ret = sscanf(buffer, "%*[^:]:%lu kB", Locked); + if (ret != 1) { + SAFE_FCLOSE(fp); + tst_brk(TBROK, "failure occurred while reading field Locked"); + return; + } + + Locked_found = true; + } + + if (Rss_found && Locked_found) { + SAFE_FCLOSE(fp); + return; + } + } + + SAFE_FCLOSE(fp); + tst_brk(TBROK, "cannot find both Rss and Locked in mapping %lx", desired_mapping_address); +} + +static void verify_mlock(void) +{ + unsigned long Locked; + unsigned long Rss; + char *buf; + + buf = SAFE_MMAP(NULL, MMAPLEN, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + SAFE_MLOCK(buf, MMAPLEN); + + get_proc_smaps_info((unsigned long)buf, &Rss, &Locked); + + // Convertion from KiB to B + Rss *= 1024; + Locked *= 1024; + + TST_EXP_EQ_LU(Rss, MMAPLEN); + TST_EXP_EQ_LU(Locked, MMAPLEN); + + SAFE_MUNLOCK(buf, MMAPLEN); + SAFE_MUNMAP(buf, MMAPLEN); +} + +static struct tst_test test = { + .test_all = verify_mlock, +}; diff --git a/testcases/kernel/syscalls/mlockall/mlockall01.c b/testcases/kernel/syscalls/mlockall/mlockall01.c index fa43dad3..50cdfc25 100755 --- a/testcases/kernel/syscalls/mlockall/mlockall01.c +++ b/testcases/kernel/syscalls/mlockall/mlockall01.c @@ -75,8 +75,6 @@ void cleanup(); char *TCID = "mlockall01"; int TST_TOTAL = 3; -#if !defined(UCLINUX) - struct test_case_t { int flag; char *fdesc; @@ -127,16 +125,6 @@ int main(int ac, char **av) tst_exit(); } -#else - -int main(void) -{ - tst_resm(TINFO, "test is not available on uClinux"); - tst_exit(); -} - -#endif - /* * setup() - performs all ONE TIME setup for this test. */ diff --git a/testcases/kernel/syscalls/mlockall/mlockall02.c b/testcases/kernel/syscalls/mlockall/mlockall02.c index 94d6e327..6524cb4a 100755 --- a/testcases/kernel/syscalls/mlockall/mlockall02.c +++ b/testcases/kernel/syscalls/mlockall/mlockall02.c @@ -99,8 +99,6 @@ struct test_case_t { 0, EINVAL, "Unknown flag"} }; -#if !defined(UCLINUX) - int main(int ac, char **av) { int lc, i; @@ -258,13 +256,3 @@ void cleanup(void) { return; } - -#else - -int main(void) -{ - tst_resm(TINFO, "test is not available on uClinux"); - tst_exit(); -} - -#endif /* if !defined(UCLINUX) */ diff --git a/testcases/kernel/syscalls/mlockall/mlockall03.c b/testcases/kernel/syscalls/mlockall/mlockall03.c index 7418dd64..a505891f 100755 --- a/testcases/kernel/syscalls/mlockall/mlockall03.c +++ b/testcases/kernel/syscalls/mlockall/mlockall03.c @@ -92,8 +92,6 @@ void cleanup(); char *TCID = "mlockall03"; int TST_TOTAL = 3; -#if !defined(UCLINUX) - char *ref_release = "2.6.8\0"; struct test_case_t { @@ -286,16 +284,6 @@ void cleanup_test(int i) } } -#else - -int main(void) -{ - tst_resm(TINFO, "test is not available on uClinux"); - tst_exit(); -} - -#endif /* if !defined(UCLINUX) */ - /* * cleanup() - performs all ONE TIME cleanup for this test at * completion or premature exit. diff --git a/testcases/kernel/syscalls/mmap/.gitignore b/testcases/kernel/syscalls/mmap/.gitignore index 4591fdbb..075be933 100755 --- a/testcases/kernel/syscalls/mmap/.gitignore +++ b/testcases/kernel/syscalls/mmap/.gitignore @@ -1,4 +1,3 @@ -/mmap001 /mmap01 /mmap02 /mmap03 @@ -18,3 +17,5 @@ /mmap18 /mmap19 /mmap20 +/mmap21 +/mmap22 diff --git a/testcases/kernel/syscalls/mmap/mmap001.c b/testcases/kernel/syscalls/mmap/mmap001.c deleted file mode 100755 index dabb7d1e..00000000 --- a/testcases/kernel/syscalls/mmap/mmap001.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2000 Juan Quintela - * Aaron Laffin - * - * 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. - * - * mmap001.c - Tests mmapping a big file and writing it once - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "test.h" - -char *TCID = "mmap001"; -int TST_TOTAL = 5; -static char *filename = NULL; -static int m_opt = 0; -static char *m_copt; - -static void cleanup(void) -{ - free(filename); - - tst_rmdir(); -} - -static void setup(void) -{ - char buf[1024]; - /* - * setup a default signal hander and a - * temporary working directory. - */ - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - snprintf(buf, 1024, "testfile.%d", getpid()); - - if ((filename = strdup(buf)) == NULL) { - tst_brkm(TBROK | TERRNO, cleanup, "strdup failed"); - } - -} - -static void help(void) -{ - printf(" -m x size of mmap in pages (default 1000)\n"); -} - -/* - * add the -m option whose parameter is the - * pages that should be mapped. - */ -option_t options[] = { - {"m:", &m_opt, &m_copt}, - {NULL, NULL, NULL} -}; - -int main(int argc, char *argv[]) -{ - char *array; - int lc; - unsigned int i; - int fd; - unsigned int pages, memsize; - - tst_parse_opts(argc, argv, options, help); - - if (m_opt) { - memsize = pages = atoi(m_copt); - - if (memsize < 1) { - tst_brkm(TBROK, cleanup, "Invalid arg for -m: %s", - m_copt); - } - - memsize *= getpagesize(); /* N PAGES */ - - } else { - /* - * default size 1000 pages; - */ - memsize = pages = 1000; - memsize *= getpagesize(); - } - - tst_resm(TINFO, "mmap()ing file of %u pages or %u bytes", pages, - memsize); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - fd = open(filename, O_RDWR | O_CREAT, 0666); - if ((fd == -1)) - tst_brkm(TBROK | TERRNO, cleanup, - "opening %s failed", filename); - - if (lseek(fd, memsize, SEEK_SET) != memsize) { - TEST_ERRNO = errno; - close(fd); - tst_brkm(TBROK | TTERRNO, cleanup, "lseek failed"); - } - - if (write(fd, "\0", 1) != 1) { - TEST_ERRNO = errno; - close(fd); - tst_brkm(TBROK | TTERRNO, cleanup, - "writing to %s failed", filename); - } - - array = mmap(0, memsize, PROT_WRITE, MAP_SHARED, fd, 0); - if (array == MAP_FAILED) { - TEST_ERRNO = errno; - close(fd); - tst_brkm(TBROK | TTERRNO, cleanup, - "mmapping %s failed", filename); - } else { - tst_resm(TPASS, "mmap() completed successfully."); - } - - tst_resm(TINFO, "touching mmaped memory"); - - for (i = 0; i < memsize; i++) { - array[i] = (char)i; - } - - /* - * seems that if the map area was bad, we'd get SEGV, - * hence we can indicate a PASS. - */ - tst_resm(TPASS, - "we're still here, mmaped area must be good"); - - TEST(msync(array, memsize, MS_SYNC)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, - "synchronizing mmapped page failed"); - } else { - tst_resm(TPASS, - "synchronizing mmapped page passed"); - } - - TEST(munmap(array, memsize)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, - "munmapping %s failed", filename); - } else { - tst_resm(TPASS, "munmapping %s successful", filename); - } - - close(fd); - unlink(filename); - - } - cleanup(); - tst_exit(); -} diff --git a/testcases/kernel/syscalls/mmap/mmap01.c b/testcases/kernel/syscalls/mmap/mmap01.c index 99266b57..9a0c01aa 100755 --- a/testcases/kernel/syscalls/mmap/mmap01.c +++ b/testcases/kernel/syscalls/mmap/mmap01.c @@ -1,194 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) 2024 Ricardo B. Marliere + * 07/2001 Ported by Wayne Boyer */ -/* - * Test Description: - * Verify that, mmap() succeeds when used to map a file where size of the - * file is not a multiple of the page size, the memory area beyond the end - * of the file to the end of the page is accessible. Also, verify that - * this area is all zeroed and the modifications done to this area are - * not written to the file. +/*\ + * Verify that mmap() succeeds when used to map a file where size of the + * file is not a multiple of the page size, the memory area beyond the end + * of the file to the end of the page is accessible. Also, verify that + * this area is all zeroed and the modifications done to this area are + * not written to the file. * - * Expected Result: - * mmap() should succeed returning the address of the mapped region. - * The memory area beyond the end of file to the end of page should be - * filled with zero. - * The changes beyond the end of file should not get written to the file. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer + * mmap() should succeed returning the address of the mapped region. + * The memory area beyond the end of file to the end of page should be + * filled with zero. The changes beyond the end of file should not get + * written to the file. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" +#include "tst_test.h" -#define TEMPFILE "mmapfile" - -char *TCID = "mmap01"; -int TST_TOTAL = 1; +#define TEMPFILE "mmapfile" +#define STRING "hello world\n" static char *addr; static char *dummy; static size_t page_sz; static size_t file_sz; static int fildes; -static char cmd_buffer[BUFSIZ]; -static void setup(void); -static void cleanup(void); - -int main(int ac, char **av) +static void check_file(void) { - int lc; + int i, fildes, buf_len = sizeof(STRING) + 3; + char buf[buf_len]; + ssize_t len; - tst_parse_opts(ac, av, NULL, NULL); + fildes = SAFE_OPEN(TEMPFILE, O_RDONLY); + len = SAFE_READ(0, fildes, buf, sizeof(buf)); + SAFE_CLOSE(fildes); - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Call mmap to map the temporary file beyond EOF - * with write access. - */ - errno = 0; - addr = mmap(NULL, page_sz, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, fildes, 0); - - /* Check for the return value of mmap() */ - if (addr == MAP_FAILED) { - tst_resm(TFAIL | TERRNO, "mmap of %s failed", TEMPFILE); - continue; - } - - /* - * Check if mapped memory area beyond EOF are - * zeros and changes beyond EOF are not written - * to file. - */ - if (memcmp(&addr[file_sz], dummy, page_sz - file_sz)) { - tst_brkm(TFAIL, cleanup, - "mapped memory area contains invalid " - "data"); - } - - /* - * Initialize memory beyond file size - */ - addr[file_sz] = 'X'; - addr[file_sz + 1] = 'Y'; - addr[file_sz + 2] = 'Z'; - - /* - * Synchronize the mapped memory region - * with the file. - */ - if (msync(addr, page_sz, MS_SYNC) != 0) { - tst_brkm(TFAIL | TERRNO, cleanup, - "failed to synchronize mapped file"); - } - - /* - * Now, Search for the pattern 'XYZ' in the - * temporary file. The pattern should not be - * found and the return value should be 1. - */ - if (system(cmd_buffer) != 0) { - tst_resm(TPASS, - "Functionality of mmap() successful"); - } else { - tst_resm(TFAIL, - "Specified pattern found in file"); - } - - /* Clean up things in case we are looping */ - /* Unmap the mapped memory */ - if (munmap(addr, page_sz) != 0) { - tst_brkm(TFAIL | TERRNO, NULL, "munmap failed"); - } + if (len != strlen(STRING)) { + tst_res(TFAIL, "Read %zi expected %zu", len, strlen(STRING)); + return; } - cleanup(); - tst_exit(); + for (i = 0; i < len; i++) + if (buf[i] == 'X' || buf[i] == 'Y' || buf[i] == 'Z') + break; + + if (i == len) + tst_res(TPASS, "Specified pattern not found in file"); + else + tst_res(TFAIL, "Specified pattern found in file"); } -static void setup(void) +static void set_file(void) { + char *write_buf = STRING; struct stat stat_buf; - char Path_name[PATH_MAX]; - char write_buf[] = "hello world\n"; - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - /* Get the path of temporary file to be created */ - if (getcwd(Path_name, sizeof(Path_name)) == NULL) { - tst_brkm(TFAIL | TERRNO, cleanup, - "getcwd failed to get current working directory"); + /* Reset file */ + if (fildes > 0) { + SAFE_CLOSE(fildes); + SAFE_UNLINK(TEMPFILE); } - /* Creat a temporary file used for mapping */ - if ((fildes = open(TEMPFILE, O_RDWR | O_CREAT, 0666)) < 0) { - tst_brkm(TFAIL, cleanup, "opening %s failed", TEMPFILE); - } + /* Create a temporary file used for mapping */ + fildes = SAFE_OPEN(TEMPFILE, O_RDWR | O_CREAT, 0666); /* Write some data into temporary file */ - if (write(fildes, write_buf, strlen(write_buf)) != (long)strlen(write_buf)) { - tst_brkm(TFAIL, cleanup, "writing to %s", TEMPFILE); - } + SAFE_WRITE(SAFE_WRITE_ALL, fildes, write_buf, strlen(write_buf)); /* Get the size of temporary file */ - if (stat(TEMPFILE, &stat_buf) < 0) { - tst_brkm(TFAIL | TERRNO, cleanup, "stat of %s failed", - TEMPFILE); - } + SAFE_STAT(TEMPFILE, &stat_buf); file_sz = stat_buf.st_size; +} - page_sz = getpagesize(); +static void run(void) +{ + set_file(); - /* Allocate and initialize dummy string of system page size bytes */ - if ((dummy = calloc(page_sz, sizeof(char))) == NULL) { - tst_brkm(TFAIL, cleanup, "calloc failed (dummy)"); + addr = SAFE_MMAP(NULL, page_sz, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fildes, 0); + + /* + * Check if mapped memory area beyond EOF are zeros and changes beyond + * EOF are not written to file. + */ + if (memcmp(&addr[file_sz], dummy, page_sz - file_sz)) + tst_brk(TFAIL, "mapped memory area contains invalid data"); + + /* + * Initialize memory beyond file size + */ + addr[file_sz] = 'X'; + addr[file_sz + 1] = 'Y'; + addr[file_sz + 2] = 'Z'; + + /* + * Synchronize the mapped memory region with the file. + */ + SAFE_MSYNC(addr, page_sz, MS_SYNC); + + /* + * Now, search for the pattern 'XYZ' in the temporary file. + * The pattern should not be found and the return value should be 1. + */ + if (!SAFE_FORK()) { + check_file(); + SAFE_MUNMAP(addr, page_sz); + exit(0); } - /* Create the command which will be executed in the test */ - sprintf(cmd_buffer, "grep XYZ %s/%s > /dev/null", Path_name, TEMPFILE); + SAFE_MUNMAP(addr, page_sz); } static void cleanup(void) { - close(fildes); - free(dummy); - tst_rmdir(); + if (dummy) + free(dummy); + + if (fildes > 0) + SAFE_CLOSE(fildes); } + +static void setup(void) +{ + page_sz = getpagesize(); + + /* Allocate and initialize dummy string of system page size bytes */ + dummy = SAFE_CALLOC(page_sz, 1); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .test_all = run, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/mmap/mmap02.c b/testcases/kernel/syscalls/mmap/mmap02.c index 7ffe61fa..21cac4b4 100755 --- a/testcases/kernel/syscalls/mmap/mmap02.c +++ b/testcases/kernel/syscalls/mmap/mmap02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, mmap() call with PROT_READ and a file descriptor which is * open for read only, succeeds to map a file creating mapped memory with * read access. diff --git a/testcases/kernel/syscalls/mmap/mmap03.c b/testcases/kernel/syscalls/mmap/mmap03.c index 9d94d265..2fa9acaf 100755 --- a/testcases/kernel/syscalls/mmap/mmap03.c +++ b/testcases/kernel/syscalls/mmap/mmap03.c @@ -1,230 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * 07/2001 Ported by Wayne Boyer + * Copyright (C) 2025 SUSE LLC Andrea Cervesato */ -/* - * Test Description: - * Call mmap() to map a file creating a mapped region with execute access - * under the following conditions - - * - The prot parameter is set to PROT_EXE - * - The file descriptor is open for read - * - The file being mapped has execute permission bit set. - * - The minimum file permissions should be 0555. +/*\ + * Map a file with mmap() syscall, creating a mapped region with execute access + * under the following conditions: * - * The call should succeed to map the file creating mapped memory with the - * required attributes. + * - file descriptor is open for read + * - minimum file permissions should be 0555 + * - file being mapped has PROT_EXEC execute permission bit set * - * Expected Result: - * mmap() should succeed returning the address of the mapped region, - * and the mapped region should contain the contents of the mapped file. - * but with ia64 and PARISC/hppa, - * an attempt to access the contents of the mapped region should give - * rise to the signal SIGSEGV. + * mmap() should succeed returning the address of the mapped region + * and the mapped region should contain the contents of the mapped file. * - * HISTORY - * 07/2001 Ported by Wayne Boyer + * On mips architecture, an attempt to access the contents of the + * mapped region should rise signal SIGSEGV. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" +#include "tst_test.h" -#define TEMPFILE "mmapfile" - -char *TCID = "mmap03"; -int TST_TOTAL = 1; +#define TEMPFILE "mmapfile" +static void *addr; +static char *data; +static char *tmpdata; static size_t page_sz; -static char *addr; -static char *dummy; -static int fildes; -static volatile int pass = 0; -static sigjmp_buf env; +static int fdesc = -1; -static void setup(void); -static void cleanup(void); -static void sig_handler(int sig); - -int main(int ac, char **av) +static void run_child(void) { - int lc; + tst_res(TINFO, "Map temporary file in memory with PROT_EXEC"); - tst_parse_opts(ac, av, NULL, NULL); + addr = SAFE_MMAP(0, page_sz, PROT_EXEC, + MAP_FILE | MAP_SHARED, fdesc, 0); - setup(); + memset(data, 0, page_sz); - for (lc = 0; TEST_LOOPING(lc); lc++) { + tst_res(TINFO, "Read data back from mapped file"); - tst_count = 0; + SAFE_READ(0, fdesc, data, page_sz); + SAFE_LSEEK(fdesc, 0, SEEK_SET); - /* - * Call mmap to map the temporary file 'TEMPFILE' - * with execute access. - */ - errno = 0; - addr = mmap(0, page_sz, PROT_EXEC, - MAP_FILE | MAP_SHARED, fildes, 0); + TST_EXP_EQ_LI(memcmp(data, tmpdata, page_sz), 0); - /* Check for the return value of mmap() */ - if (addr == MAP_FAILED) { - tst_resm(TFAIL | TERRNO, "mmap() failed on %s", - TEMPFILE); - continue; - } + SAFE_MUNMAP(addr, page_sz); +} - /* - * Read the file contents into the dummy - * variable. - */ - if (read(fildes, dummy, page_sz) < 0) { - tst_brkm(TFAIL | TERRNO, cleanup, - "reading %s failed", TEMPFILE); - } - - /* - * Check whether the mapped memory region - * has the file contents. - * - * with ia64 and PARISC/hppa, this should - * generate a SIGSEGV which will be caught below. - * - */ - - if (sigsetjmp(env, 1) == 0) { - if (memcmp(dummy, addr, page_sz)) { - tst_resm(TFAIL, - "mapped memory region " - "contains invalid data"); - } else { - tst_resm(TPASS, - "mmap() functionality is " - "correct"); - } - } -#if defined(__ia64__) || defined(__hppa__) || defined(__mips__) - if (pass) { - tst_resm(TPASS, "Got SIGSEGV as expected"); - } else { - tst_resm(TFAIL, "Mapped memory region with NO " - "access is accessible"); - } -#endif - - /* Clean up things in case we are looping */ - /* Unmap the mapped memory */ - if (munmap(addr, page_sz) != 0) { - tst_brkm(TFAIL | TERRNO, cleanup, - "failed to unmap the mmapped pages"); - } - pass = 0; +static void run(void) +{ + pid_t pid; + int status; + pid = SAFE_FORK(); + if (!pid) { + run_child(); + exit(0); } - cleanup(); - tst_exit(); + SAFE_WAITPID(pid, &status, 0); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + tst_res(TPASS, "Use of return in child didn't cause abnormal exit"); + return; + } + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "Use of return in child caused SIGSEGV"); + return; + } + + tst_res(TFAIL, "Mapped memory region with NO access is accessible"); } static void setup(void) { - char *tst_buff; - - tst_sig(NOFORK, sig_handler, cleanup); - - TEST_PAUSE; - page_sz = getpagesize(); - /* Allocate space for the test buffer */ - if ((tst_buff = calloc(page_sz, sizeof(char))) == NULL) { - tst_brkm(TFAIL, NULL, "calloc failed (tst_buff)"); - } + tmpdata = SAFE_MALLOC(page_sz); + memset(tmpdata, 'a', page_sz); - /* Fill the test buffer with the known data */ - memset(tst_buff, 'A', page_sz); + tst_res(TINFO, "Create temporary file"); - tst_tmpdir(); + tst_fill_file(TEMPFILE, 'a', page_sz, 1); + fdesc = SAFE_OPEN(TEMPFILE, O_RDONLY, 0555); - /* Creat a temporary file used for mapping */ - if ((fildes = open(TEMPFILE, O_WRONLY | O_CREAT, 0666)) < 0) { - free(tst_buff); - tst_brkm(TFAIL | TERRNO, cleanup, "opening %s failed", - TEMPFILE); - } - - /* Write test buffer contents into temporary file */ - if (write(fildes, tst_buff, page_sz) < (long)page_sz) { - free(tst_buff); - tst_brkm(TFAIL | TERRNO, cleanup, "writing to %s failed", - TEMPFILE); - } - - /* Free the memory allocated for test buffer */ - free(tst_buff); - - /* Make sure proper permissions set on file */ - if (fchmod(fildes, 0555) < 0) { - tst_brkm(TFAIL, cleanup, "fchmod of %s failed", TEMPFILE); - } - - /* Close the temporary file opened for write */ - if (close(fildes) < 0) { - tst_brkm(TFAIL | TERRNO, cleanup, "closing %s failed", - TEMPFILE); - } - - /* Allocate and initialize dummy string of system page size bytes */ - if ((dummy = calloc(page_sz, sizeof(char))) == NULL) { - tst_brkm(TFAIL, cleanup, "calloc failed (dummy)"); - } - - /* Open the temporary file again for reading */ - if ((fildes = open(TEMPFILE, O_RDONLY)) < 0) { - tst_brkm(TFAIL | TERRNO, cleanup, - "opening %s read-only failed", TEMPFILE); - } -} - -/* - * This function gets executed when the test process receives - * the signal SIGSEGV while trying to access the contents of memory which - * is not accessible. - */ -static void sig_handler(int sig) -{ - if (sig == SIGSEGV) { - /* set the global variable and jump back */ - pass = 1; - siglongjmp(env, 1); - } else - tst_brkm(TBROK, cleanup, "received an unexpected signal"); + data = SAFE_MALLOC(page_sz); } static void cleanup(void) { - close(fildes); - free(dummy); - tst_rmdir(); + if (data) + free(data); + + if (tmpdata) + free(tmpdata); + + if (fdesc != -1) + SAFE_CLOSE(fdesc); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/mmap/mmap04.c b/testcases/kernel/syscalls/mmap/mmap04.c index 43f7b752..5b28180d 100755 --- a/testcases/kernel/syscalls/mmap/mmap04.c +++ b/testcases/kernel/syscalls/mmap/mmap04.c @@ -1,185 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * 07/2001 Ported by Wayne Boyer + * Copyright (c) 2023 SUSE LLC Avinesh Kumar */ -/* - * Test Description: - * Call mmap() to map a file creating a mapped region with read/exec access - * under the following conditions - - * - The prot parameter is set to PROT_READ|PROT_EXEC - * - The file descriptor is open for read - * - The file being mapped has read and execute permission bit set. - * - The minimum file permissions should be 0555. - * - * The call should succeed to map the file creating mapped memory with the - * required attributes. - * - * Expected Result: - * mmap() should succeed returning the address of the mapped region, - * and the mapped region should contain the contents of the mapped file. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer +/*\ + * Verify that, after a successful mmap() call, permission bits of the new + * mapping in /proc/pid/maps file matches the prot and flags arguments in + * mmap() call. */ +#include +#include "tst_test.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" +static char *addr1; +static char *addr2; -#define TEMPFILE "mmapfile" +static struct tcase { + int prot; + int flags; + char *exp_perms; +} tcases[] = { + {PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, "---p"}, + {PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, "---s"}, + {PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, "r--p"}, + {PROT_READ, MAP_ANONYMOUS | MAP_SHARED, "r--s"}, + {PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, "-w-p"}, + {PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, "-w-s"}, + {PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, "rw-p"}, + {PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, "rw-s"}, + {PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, "r-xp"}, + {PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED, "r-xs"}, + {PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, "-wxp"}, + {PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED, "-wxs"}, + {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, "rwxp"}, + {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED, "rwxs"} +}; -char *TCID = "mmap04"; -int TST_TOTAL = 1; - -static size_t page_sz; -static char *addr; -static char *dummy; -static int fildes; - -static void setup(void); -static void cleanup(void); - -int main(int ac, char **av) +static void run(unsigned int i) { - int lc; + struct tcase *tc = &tcases[i]; + char perms[8]; + char fmt[1024]; + unsigned int pagesize; + int flag; - tst_parse_opts(ac, av, NULL, NULL); + pagesize = SAFE_SYSCONF(_SC_PAGESIZE); - setup(); + /* To avoid new mapping getting merged with existing mappings, we first + * create a 2-page mapping with the different permissions, and then remap + * the 2nd page with the perms being tested. + */ + flag = (tc->flags & MAP_PRIVATE) ? MAP_SHARED : MAP_PRIVATE; + addr1 = SAFE_MMAP(NULL, pagesize * 2, PROT_NONE, MAP_ANONYMOUS | flag, -1, 0); - for (lc = 0; TEST_LOOPING(lc); lc++) { + addr2 = SAFE_MMAP(addr1 + pagesize, pagesize, tc->prot, tc->flags | MAP_FIXED, -1, 0); - tst_count = 0; + /* A /proc/self/maps address is at least 8 hex (left zero padded) */ + sprintf(fmt, "%08" PRIxPTR "-%%*x %%s", (uintptr_t)addr2); + SAFE_FILE_LINES_SCANF("/proc/self/maps", fmt, perms); - /* - * Call mmap to map the temporary file 'TEMPFILE' - * with read and execute access. - */ - errno = 0; - addr = mmap(0, page_sz, PROT_READ | PROT_EXEC, - MAP_FILE | MAP_SHARED, fildes, 0); - - /* Check for the return value of mmap() */ - if (addr == MAP_FAILED) { - tst_resm(TFAIL | TERRNO, "mmap of %s failed", TEMPFILE); - continue; - } - - /* - * Read the file contents into the dummy - * variable. - */ - if (read(fildes, dummy, page_sz) < 0) { - tst_brkm(TFAIL, cleanup, "reading %s failed", - TEMPFILE); - } - - /* - * Check whether the mapped memory region - * has the file contents. - */ - if (memcmp(dummy, addr, page_sz)) { - tst_resm(TFAIL, - "mapped memory region contains invalid " - "data"); - } else { - tst_resm(TPASS, - "Functionality of mmap() successful"); - } - - /* Clean up things in case we are looping. */ - /* Unmap the mapped memory */ - if (munmap(addr, page_sz) != 0) { - tst_brkm(TFAIL, cleanup, "munmapping failed"); - } + if (!strcmp(perms, tc->exp_perms)) { + tst_res(TPASS, "mapping permissions in /proc matched: %s", perms); + } else { + tst_res(TFAIL, "mapping permissions in /proc mismatched, expected: %s, found: %s", + tc->exp_perms, perms); } - cleanup(); - tst_exit(); + SAFE_MUNMAP(addr1, pagesize * 2); } -static void setup(void) -{ - char *tst_buff; - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - page_sz = getpagesize(); - - if ((tst_buff = calloc(page_sz, sizeof(char))) == NULL) { - tst_brkm(TFAIL, NULL, "calloc failed (tst_buff)"); - } - - /* Fill the test buffer with the known data */ - memset(tst_buff, 'A', page_sz); - - tst_tmpdir(); - - /* Creat a temporary file used for mapping */ - if ((fildes = open(TEMPFILE, O_WRONLY | O_CREAT, 0666)) < 0) { - free(tst_buff); - tst_brkm(TFAIL, cleanup, "opening %s failed", TEMPFILE); - } - - /* Write test buffer contents into temporary file */ - if (write(fildes, tst_buff, page_sz) < (ssize_t)page_sz) { - free(tst_buff); - tst_brkm(TFAIL, cleanup, "writing to %s failed", TEMPFILE); - } - - /* Free the memory allocated for test buffer */ - free(tst_buff); - - /* Make sure proper permissions set on file */ - if (fchmod(fildes, 0555) < 0) { - tst_brkm(TFAIL, cleanup, "fchmod of %s failed", TEMPFILE); - } - - /* Close the temporary file opened for write */ - if (close(fildes) < 0) { - tst_brkm(TFAIL, cleanup, "closing %s failed", TEMPFILE); - } - - /* Allocate and initialize dummy string of system page size bytes */ - if ((dummy = calloc(page_sz, sizeof(char))) == NULL) { - tst_brkm(TFAIL, cleanup, "calloc failed (dummy)"); - } - - /* Open the temporary file again for reading */ - if ((fildes = open(TEMPFILE, O_RDONLY)) < 0) { - tst_brkm(TFAIL, cleanup, - "opening %s read-only failed", TEMPFILE); - } -} - -static void cleanup(void) -{ - close(fildes); - free(dummy); - tst_rmdir(); -} +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), +}; diff --git a/testcases/kernel/syscalls/mmap/mmap05.c b/testcases/kernel/syscalls/mmap/mmap05.c index 7abddaa9..445b5a33 100755 --- a/testcases/kernel/syscalls/mmap/mmap05.c +++ b/testcases/kernel/syscalls/mmap/mmap05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, mmap() call with 'PROT_NONE' and a file descriptor which is * open for read and write, succeeds to map the file creating mapped memory, * but any attempt to access the contents of the mapped region causes the diff --git a/testcases/kernel/syscalls/mmap/mmap06.c b/testcases/kernel/syscalls/mmap/mmap06.c index 615743fa..3c2ca0d0 100755 --- a/testcases/kernel/syscalls/mmap/mmap06.c +++ b/testcases/kernel/syscalls/mmap/mmap06.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, mmap() call fails with errno: * * - EACCES, when a file mapping is requested but the file descriptor is not open for reading. @@ -56,16 +54,10 @@ static void run(unsigned int i) { struct tcase *tc = &tcases[i]; - TESTPTR(mmap(NULL, tc->length, tc->prot, tc->flags, fd, 0)); + TST_EXP_FAIL_PTR_VOID(mmap(NULL, tc->length, tc->prot, tc->flags, fd, 0), tc->exp_errno); - if (TST_RET_PTR != MAP_FAILED) { - tst_res(TFAIL, "mmap() was successful unexpectedly"); - SAFE_MUNMAP(TST_RET_PTR, MMAPSIZE); - } else if (TST_ERR == tc->exp_errno) { - tst_res(TPASS | TERRNO, "mmap() failed with"); - } else { - tst_res(TFAIL | TERRNO, "mmap() failed unexpectedly"); - } + if (TST_RET_PTR != MAP_FAILED) + SAFE_MUNMAP(TST_RET_PTR, page_sz); } static void cleanup(void) diff --git a/testcases/kernel/syscalls/mmap/mmap08.c b/testcases/kernel/syscalls/mmap/mmap08.c index 5c9fd782..4cb9a46f 100755 --- a/testcases/kernel/syscalls/mmap/mmap08.c +++ b/testcases/kernel/syscalls/mmap/mmap08.c @@ -6,9 +6,7 @@ */ /*\ - * [Description] - * - * verify that, mmap() calls fails with errno EBADF when a file mapping + * Verify that, mmap() calls fails with errno EBADF when a file mapping * is requested but the fd is not a valid file descriptor. */ @@ -27,16 +25,11 @@ static void setup(void) static void run(void) { - TESTPTR(mmap(NULL, page_sz, PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0)); + TST_EXP_FAIL_PTR_VOID(mmap(NULL, page_sz, PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0), EBADF); - if (TST_RET_PTR != MAP_FAILED) { - tst_res(TFAIL, "mmap() passed unexpectedly"); + if (TST_RET_PTR != MAP_FAILED) SAFE_MUNMAP(TST_RET_PTR, page_sz); - } else if (TST_ERR == EBADF) { - tst_res(TPASS, "mmap() failed with EBADF"); - } else { - tst_res(TFAIL | TERRNO, "mmap() failed with an invalid errno"); - } } static void cleanup(void) diff --git a/testcases/kernel/syscalls/mmap/mmap09.c b/testcases/kernel/syscalls/mmap/mmap09.c index 4ab0da47..8c6b514a 100755 --- a/testcases/kernel/syscalls/mmap/mmap09.c +++ b/testcases/kernel/syscalls/mmap/mmap09.c @@ -1,119 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * Copyright (c) International Business Machines Corp., 2003 - * - * 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 + * Author: by Paul Larson + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -/* - * Test Description: - * Verify that truncating a mmaped file works correctly. +/*\ + * Verify that truncating a mmaped file works correctly. * - * Expected Result: - * ftruncate should be allowed to increase, decrease, or zero the - * size of a file that has been mmaped + * Use ftruncate to: * - * Test: - * Use ftruncate to shrink the file while it is mapped - * Use ftruncate to grow the file while it is mapped - * Use ftruncate to zero the size of the file while it is mapped - * - * HISTORY - * 04/2003 Written by Paul Larson + * 1. shrink the file while it is mapped + * 2. grow the file while it is mapped + * 3. zero the size of the file while it is mapped */ -#include -#include -#include -#include -#include -#include -#include "test.h" -#define mapsize (1 << 14) +#include "tst_test.h" -char *TCID = "mmap09"; -int TST_TOTAL = 3; +/* size of the test file = 64 KB */ +#define MAPSIZE (64 * 1024) static int fd; static char *maddr; -static struct test_case_t { +static struct test_case +{ off_t newsize; char *desc; -} TC[] = { - {mapsize - 8192, "ftruncate mmaped file to a smaller size"}, - {mapsize + 1024, "ftruncate mmaped file to a larger size"}, +} tcases[] = { + {MAPSIZE - 8192, "ftruncate mmaped file to a smaller size"}, + {MAPSIZE + 1024, "ftruncate mmaped file to a larger size"}, {0, "ftruncate mmaped file to 0 size"}, }; -static void setup(void); -static void cleanup(void); - -int main(int argc, char **argv) +static void verify_mmap(unsigned int nr) { - int lc; - int i; + struct test_case *tc = &tcases[nr]; - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - for (i = 0; i < TST_TOTAL; i++) { - TEST(ftruncate(fd, TC[i].newsize)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "%s", TC[i].desc); - } else { - tst_resm(TPASS, "%s", TC[i].desc); - } - } - - } - - cleanup(); - tst_exit(); + TST_EXP_PASS(ftruncate(fd, tc->newsize), "%s", tc->desc); } static void setup(void) { - tst_sig(NOFORK, DEF_HANDLER, cleanup); + fd = SAFE_OPEN("/tmp/mmaptest", O_RDWR | O_CREAT, 0666); - TEST_PAUSE; - - tst_tmpdir(); - - if ((fd = open("mmaptest", O_RDWR | O_CREAT, 0666)) < 0) - tst_brkm(TFAIL | TERRNO, cleanup, "opening mmaptest failed"); - - /* ftruncate the file to 16k */ - if (ftruncate(fd, mapsize) < 0) - tst_brkm(TFAIL | TERRNO, cleanup, "ftruncate file failed"); - - maddr = mmap(0, mapsize, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, fd, 0); - if (maddr == MAP_FAILED) - tst_brkm(TFAIL | TERRNO, cleanup, "mmapping mmaptest failed"); + /* set the file to initial size */ + SAFE_FTRUNCATE(fd, MAPSIZE); + maddr = SAFE_MMAP(0, MAPSIZE, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); /* fill up the file with A's */ - memset(maddr, 'A', mapsize); + memset(maddr, 'A', MAPSIZE); } static void cleanup(void) { - munmap(maddr, mapsize); - close(fd); - tst_rmdir(); + if (maddr) + SAFE_MUNMAP(maddr, MAPSIZE); + + if (fd) + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test = verify_mmap, + .tcnt = ARRAY_SIZE(tcases), + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/mmap/mmap10.c b/testcases/kernel/syscalls/mmap/mmap10.c index b844af07..fb554389 100755 --- a/testcases/kernel/syscalls/mmap/mmap10.c +++ b/testcases/kernel/syscalls/mmap/mmap10.c @@ -1,206 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2010 Red Hat, Inc. - * 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. + * Copyright (C) 2025 SUSE LLC Andrea Cervesato */ -/* - * mmap/munmap /dev/zero: a common way of malloc()/free() anonymous - * memory on Solaris. +/*\ + * This test examines the functionality of mapping and unmapping /dev/zero, + * which is a common method for allocating anonymous memory in Solaris. * - * The basic purpose of this is a to test if it is possible to map and - * unmap /dev/zero, and to read and write the mapping. Being inspired - * by two bugs in the past, the design of the test was added some - * variations based on the reproducers for them. It also accept an - * option to mmap/munmap anonymous pages. + * The primary objective is to determine whether it is possible to successfully + * map and unmap /dev/zero, as well as to read from and write to the mapped + * memory. The design of this test is inspired by two previous bugs, + * incorporating variations based on their reproducers. Additionally, the test + * accepts an option to mmap/munmap anonymous pages. * - * One is to trigger panic with transparent hugepage feature that - * split_huge_page is very strict in checking the rmap walk was - * perfect. Keep it strict because if page_mapcount isn't stable and - * just right, the __split_huge_page_refcount that follows the rmap - * walk could lead to erratic page_count()s for the subpages. The bug - * in fork lead to the rmap walk finding the parent huge-pmd twice - * instead of just one, because the anon_vma_chain objects of the - * child vma still point to the vma->vm_mm of the parent. That trips - * on the split_huge_page mapcount vs page_mapcount check leading to a - * BUG_ON. + * One of the bugs aims to trigger a panic related to the transparent hugepage + * feature. The split_huge_page function is particularly strict in verifying + * that the reverse mapping (rmap) walk is accurate. This strictness is crucial + * because if the page_mapcount is not stable or correct, the subsequent + * __split_huge_page_refcount operation could lead to inconsistent page_count() + * values for the subpages. A bug related to fork caused the rmap walk to find + * the parent huge-pmd twice instead of once, due to the anon_vma_chain objects + * of the child VMA still pointing to the parent's vma->vm_mm. This + * inconsistency triggers a failure in the split_huge_page mapcount versus + * page_mapcount check, resulting in a BUG_ON. * - * The other bug is mmap() of /dev/zero results in calling map_zero() - * which on RHEL5 maps the ZERO_PAGE in every PTE within that virtual - * address range. Since the application which maps a region from 5M to - * 16M in size is also multi-threaded the subsequent munmap() of - * /dev/zero results is TLB shootdowns to all other CPUs. When this - * happens thousands or millions of times the application performance - * is terrible. The mapping ZERO_PAGE in every pte within that virtual - * address range was an optimization to make the subsequent pagefault - * times faster on RHEL5 that has been removed/changed upstream. + * The second bug involves the mmap() operation on /dev/zero, which invokes + * map_zero(). On RHEL5, this function maps the ZERO_PAGE into every page table + * entry (PTE) within the specified virtual address range. When an application + * maps a region from 5M to 16M and operates in a multi-threaded environment, + * the subsequent munmap() of /dev/zero leads to TLB shootdowns across all CPUs. + * When this occurs thousands or millions of times, it severely degrades + * application performance. The optimization of mapping the ZERO_PAGE in every + * PTE within that virtual address range was intended to enhance page fault + * handling times on RHEL5 but has since been modified or removed in upstream + * versions. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "config.h" -#define SIZE (5*1024*1024) +#include "tst_test.h" + +#define SIZE (5 * TST_MB) #define PATH_KSM "/sys/kernel/mm/ksm/" -char *TCID = "mmap10"; -int TST_TOTAL = 1; +static size_t page_sz; +static char *memory; -static int fd, opt_anon, opt_ksm; -static long ps; -static char *x; - -void setup(void); -void cleanup(void); -void mmapzero(void); -void help(void); - -static option_t options[] = { - {"a", &opt_anon, NULL}, - {"s", &opt_ksm, NULL}, - {NULL, NULL, NULL} +struct tcase { + int anon; + int add_ksm; +} tcases[] = { + { .anon = 1 }, + { .add_ksm = 1 }, + { .anon = 1, .add_ksm = 1 }, }; -int main(int argc, char *argv[]) +static void run(unsigned int i) { - int lc; + struct tcase *tc = &tcases[i]; + int fd = -1; - tst_parse_opts(argc, argv, options, help); - - if (opt_ksm) { + if (tc->add_ksm) { if (access(PATH_KSM, F_OK) == -1) - tst_brkm(TCONF, NULL, - "KSM configuration is not enabled"); -#ifdef HAVE_DECL_MADV_MERGEABLE - tst_resm(TINFO, "add to KSM regions."); -#else - tst_brkm(TCONF, NULL, "MADV_MERGEABLE missing in sys/mman.h"); -#endif + tst_brk(TCONF, "KSM configuration is not enabled"); + else + tst_res(TINFO, "Add to KSM regions"); } - if (opt_anon) - tst_resm(TINFO, "use anonymous pages."); + + if (tc->anon) + tst_res(TINFO, "Use anonymous pages"); else - tst_resm(TINFO, "use /dev/zero."); + tst_res(TINFO, "Use /dev/zero device"); - setup(); - - tst_resm(TINFO, "start tests."); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - mmapzero(); - } - - cleanup(); - tst_exit(); -} - -void mmapzero(void) -{ - int n; - - if (opt_anon) { - x = mmap(NULL, SIZE + SIZE - ps, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (tc->anon) { + memory = SAFE_MMAP(NULL, SIZE + SIZE - page_sz, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } else { - if ((fd = open("/dev/zero", O_RDWR, 0666)) < 0) - tst_brkm(TBROK | TERRNO, cleanup, "open"); - x = mmap(NULL, SIZE + SIZE - ps, PROT_READ | PROT_WRITE, - MAP_PRIVATE, fd, 0); - } - if (x == MAP_FAILED) - tst_brkm(TFAIL | TERRNO, cleanup, "mmap"); -#ifdef HAVE_DECL_MADV_MERGEABLE - if (opt_ksm) { - if (madvise(x, SIZE + SIZE - ps, MADV_MERGEABLE) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "madvise"); - } -#endif - x[SIZE] = 0; + fd = SAFE_OPEN("/dev/zero", O_RDWR, 0666); - switch (n = fork()) { - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork"); - case 0: - if (munmap(x + SIZE + ps, SIZE - ps - ps) == -1) - tst_brkm(TFAIL | TERRNO, cleanup, "munmap"); - exit(0); - default: - break; + memory = SAFE_MMAP(NULL, SIZE + SIZE - page_sz, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, fd, 0); } - switch (n = fork()) { - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork"); - case 0: - if (munmap(x + SIZE + ps, SIZE - ps - ps) == -1) - tst_brkm(TFAIL | TERRNO, cleanup, - "subsequent munmap #1"); - exit(0); - default: - switch (n = fork()) { - case -1: - tst_brkm(TBROK | TERRNO, cleanup, "fork"); - case 0: - if (munmap(x + SIZE + ps, SIZE - ps - ps) == -1) - tst_brkm(TFAIL | TERRNO, cleanup, - "subsequent munmap #2"); + if (tc->add_ksm) { + if (madvise(memory, SIZE + SIZE - page_sz, MADV_MERGEABLE) == -1) + tst_brk(TBROK | TERRNO, "madvise error"); + } + + memory[SIZE] = 0; + + for (int i = 0; i < 3; i++) { + if (!SAFE_FORK()) { + SAFE_MUNMAP(memory + SIZE + page_sz, SIZE - page_sz * 2); exit(0); - default: - break; } - break; } - if (munmap(x, SIZE + SIZE - ps) == -1) - tst_resm(TFAIL | TERRNO, "munmap all"); + SAFE_MUNMAP(memory, SIZE + SIZE - page_sz); - while (waitpid(-1, &n, WUNTRACED | WCONTINUED) > 0) - if (WEXITSTATUS(n) != 0) - tst_resm(TFAIL, "child exit status is %d", - WEXITSTATUS(n)); + tst_reap_children(); + + tst_res(TPASS, "All memory has been released"); + + if (fd != -1) + SAFE_CLOSE(fd); } -void cleanup(void) +static void setup(void) { + page_sz = SAFE_SYSCONF(_SC_PAGESIZE); } -void setup(void) -{ - tst_require_root(); - - tst_sig(FORK, DEF_HANDLER, cleanup); - TEST_PAUSE; - - if ((ps = sysconf(_SC_PAGESIZE)) == -1) - tst_brkm(TBROK | TERRNO, cleanup, "sysconf(_SC_PAGESIZE)"); -} - -void help(void) -{ - printf(" -a Test anonymous pages\n"); - printf(" -s Add to KSM regions\n"); -} +static struct tst_test test = { + .test = run, + .setup = setup, + .needs_root = 1, + .forks_child = 1, + .tcnt = ARRAY_SIZE(tcases), +}; diff --git a/testcases/kernel/syscalls/mmap/mmap12.c b/testcases/kernel/syscalls/mmap/mmap12.c index 995a2bab..04321883 100755 --- a/testcases/kernel/syscalls/mmap/mmap12.c +++ b/testcases/kernel/syscalls/mmap/mmap12.c @@ -1,31 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 FNST, DAN LI + * Copyright (C) 2025 SUSE LLC Andrea Cervesato */ -/* - * Test Description: - * Verify MAP_POPULATE works fine. - * "For a file mapping, this causes read-ahead on the file. - * Later accesses to the mapping will not be blocked by page faults" - * - * Expected Result: - * mmap() with MAP_POPULATE should succeed returning the address of the - * mapped region and this file has been read into RAM, so pages should - * be present. +/*\ + * Verify that mmap() with MAP_POPULATE succeed returning the address of the + * mapped region. The file should be read into RAM, and pages should be present. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "tst_test.h" @@ -60,9 +42,9 @@ static void page_check(void) tst_res(TCONF | TERRNO, "don't have permission to open dev pagemap"); return; - } else { - tst_brk(TFAIL | TERRNO, "pen dev pagemap failed"); } + + tst_brk(TFAIL | TERRNO, "pen dev pagemap failed"); } offset = SAFE_LSEEK(pm, index, SEEK_SET); diff --git a/testcases/kernel/syscalls/mmap/mmap13.c b/testcases/kernel/syscalls/mmap/mmap13.c index c5a2058e..34ac1a53 100755 --- a/testcases/kernel/syscalls/mmap/mmap13.c +++ b/testcases/kernel/syscalls/mmap/mmap13.c @@ -1,142 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 FNST, DAN LI - * - * 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 + * Copyright (c) 2024 SUSE LLC Avinesh Kumar */ -/* - * Test Description: - * Verify error signal SIGBUS. - * "Attempted access to a portion of the buffer that does not correspond - * to the file." - * - * Expected Result: - * mmap() should succeed returning the address of the mapped region, - * and an attempt to access the memory which does not correspond to the file - * should rise the signal SIGBUS. +/*\ + * Verify that, mmap() call succeeds to create a file mapping with length + * argument greater than the file size but any attempt to reference the + * memory region which does not correspond to the file causes SIGBUS signal. */ -#include + #include -#include -#include -#include -#include -#include -#include -#include -#include #include - -#include "test.h" +#include "tst_test.h" #define TEMPFILE "mmapfile" - -char *TCID = "mmap13"; -int TST_TOTAL = 1; - static size_t page_sz; static char *addr; -static int fildes; +static int fd; static volatile sig_atomic_t pass; static sigjmp_buf env; -static void setup(void); -static void cleanup(void); -static void sig_handler(int sig); - -int main(int argc, char *argv[]) -{ - int lc; - char *ch; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - addr = mmap(NULL, page_sz * 2, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, fildes, 0); - - if (addr == MAP_FAILED) { - tst_resm(TFAIL | TERRNO, "mmap() failed on %s", - TEMPFILE); - continue; - } - - if (sigsetjmp(env, 1) == 0) { - ch = addr + page_sz + 1; - *ch = 0; - } - - if (pass) - tst_resm(TPASS, "Got SIGBUS " - "as expected"); - else - tst_resm(TFAIL, "Invalid access not " - "rise SIGBUS"); - - if (munmap(addr, page_sz * 2) != 0) - tst_brkm(TFAIL | TERRNO, cleanup, - "failed to unmap the mmapped pages"); - - pass = 0; - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_sig(NOFORK, sig_handler, cleanup); - - TEST_PAUSE; - - page_sz = getpagesize(); - - tst_tmpdir(); - - fildes = open(TEMPFILE, O_RDWR | O_CREAT, 0766); - if (fildes < 0) - tst_brkm(TFAIL | TERRNO, cleanup, "opening %s failed", - TEMPFILE); - - if (ftruncate(fildes, page_sz / 2) == -1) - tst_brkm(TFAIL | TERRNO, cleanup, "ftruncate %s failed", - TEMPFILE); -} - -/* - * This function gets executed when the test process receives - * the signal SIGBUS while trying to access the memory which - * does not correspond to the file. - */ static void sig_handler(int sig) { if (sig == SIGBUS) { pass = 1; siglongjmp(env, 1); - } else { - tst_brkm(TBROK, cleanup, "received an unexpected signal"); } } +static void setup(void) +{ + SAFE_SIGNAL(SIGBUS, sig_handler); + + page_sz = getpagesize(); + + fd = SAFE_OPEN(TEMPFILE, O_RDWR | O_CREAT, 0666); + SAFE_FTRUNCATE(fd, page_sz / 2); +} + +static void run(void) +{ + char *ch; + + addr = mmap(0, page_sz * 2, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + tst_res(TFAIL | TERRNO, "mmap() of %s failed", TEMPFILE); + return; + } + + if (sigsetjmp(env, 1) == 0) { + ch = addr + page_sz + 1; + *ch = 0; + } + + if (pass == 1) + tst_res(TPASS, "Received SIGBUS signal as expected"); + else + tst_res(TFAIL, "SIGBUS signal not received"); + + SAFE_MUNMAP(addr, page_sz * 2); +} + static void cleanup(void) { - close(fildes); - tst_rmdir(); + if (fd > 0) + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .needs_tmpdir = 1 +}; diff --git a/testcases/kernel/syscalls/mmap/mmap14.c b/testcases/kernel/syscalls/mmap/mmap14.c index 31632601..7bded88c 100755 --- a/testcases/kernel/syscalls/mmap/mmap14.c +++ b/testcases/kernel/syscalls/mmap/mmap14.c @@ -1,124 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 FNST, DAN LI - * - * 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 + * Copyright (c) 2024 Ricardo B. Marliere */ -/* - * Test Description: - * Verify MAP_LOCKED works fine. - * "Lock the pages of the mapped region into memory in the manner of mlock(2)." +/*\ + * Verify basic functionality of mmap(2) with MAP_LOCKED. * - * Expected Result: - * mmap() should succeed returning the address of the mapped region, - * and this region should be locked into memory. + * mmap(2) should succeed returning the address of the mapped region, + * and this region should be locked into memory. */ #include #include -#include "test.h" +#include "tst_test.h" +#include "tst_safe_stdio.h" #define TEMPFILE "mmapfile" #define MMAPSIZE (1UL<<20) #define LINELEN 256 -char *TCID = "mmap14"; -int TST_TOTAL = 1; - static char *addr; -static void getvmlck(unsigned int *lock_sz); -static void setup(void); -static void cleanup(void); - -int main(int argc, char *argv[]) +static void getvmlck(unsigned int *lock_sz) { - int lc; - unsigned int sz_before; - unsigned int sz_after; - unsigned int sz_ch; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - getvmlck(&sz_before); - - addr = mmap(NULL, MMAPSIZE, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_LOCKED | MAP_ANONYMOUS, - -1, 0); - - if (addr == MAP_FAILED) { - tst_resm(TFAIL | TERRNO, "mmap of %s failed", TEMPFILE); - continue; - } - - getvmlck(&sz_after); - - sz_ch = sz_after - sz_before; - if (sz_ch == MMAPSIZE / 1024) { - tst_resm(TPASS, "Functionality of mmap() " - "successful"); - } else { - tst_resm(TFAIL, "Expected %luK locked, " - "get %uK locked", - MMAPSIZE / 1024, sz_ch); - } - - if (munmap(addr, MMAPSIZE) != 0) - tst_brkm(TFAIL | TERRNO, NULL, "munmap failed"); - } - - cleanup(); - tst_exit(); -} - -void getvmlck(unsigned int *lock_sz) -{ - int ret; char line[LINELEN]; FILE *fstatus = NULL; - fstatus = fopen("/proc/self/status", "r"); - if (fstatus == NULL) - tst_brkm(TFAIL | TERRNO, NULL, "Open dev status failed"); + fstatus = SAFE_FOPEN("/proc/self/status", "r"); while (fgets(line, LINELEN, fstatus) != NULL) if (strstr(line, "VmLck") != NULL) break; - ret = sscanf(line, "%*[^0-9]%d%*[^0-9]", lock_sz); - if (ret != 1) - tst_brkm(TFAIL | TERRNO, NULL, "Get lock size failed"); + SAFE_SSCANF(line, "%*[^0-9]%d%*[^0-9]", lock_sz); - fclose(fstatus); + SAFE_FCLOSE(fstatus); } -static void setup(void) +static void run(void) { - tst_require_root(); + unsigned int sz_before; + unsigned int sz_after; + unsigned int sz_ch; - tst_sig(FORK, DEF_HANDLER, cleanup); + getvmlck(&sz_before); - TEST_PAUSE; + addr = mmap(NULL, MMAPSIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_LOCKED | MAP_ANONYMOUS, -1, 0); + + if (addr == MAP_FAILED) { + tst_res(TFAIL | TERRNO, "mmap() of %s failed", TEMPFILE); + return; + } + + getvmlck(&sz_after); + + sz_ch = sz_after - sz_before; + if (sz_ch == MMAPSIZE / 1024) { + tst_res(TPASS, "mmap() locked %uK", sz_ch); + } else { + tst_res(TFAIL, "Expected %luK locked, get %uK locked", + MMAPSIZE / 1024, sz_ch); + } + + SAFE_MUNMAP(addr, MMAPSIZE); } -static void cleanup(void) -{ -} +static struct tst_test test = { + .needs_root = 1, + .test_all = run, +}; diff --git a/testcases/kernel/syscalls/mmap/mmap15.c b/testcases/kernel/syscalls/mmap/mmap15.c index 443a37eb..6681617a 100755 --- a/testcases/kernel/syscalls/mmap/mmap15.c +++ b/testcases/kernel/syscalls/mmap/mmap15.c @@ -1,113 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2004 - * Written by Robbie Williamson - * - * 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 + * Written by Robbie Williamson + * Copyright (c) 2023 SUSE LLC Avinesh Kumar + * Copyright (c) Linux Test Project, 2014-2023 */ -/* - * Test Description: Test that a normal page cannot be mapped into a high - * memory region. +/*\ + * Verify that, a normal page cannot be mapped into a high memory region, + * and mmap() call fails with either ENOMEM or EINVAL errno. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" -#include "lapi/abisize.h" - -char *TCID = "mmap15"; -int TST_TOTAL = 1; +#include "tst_test.h" #ifdef __ia64__ -# define HIGH_ADDR (void *)(0xa000000000000000UL) +# define HIGH_ADDR ((void *)(0xa000000000000000UL)) #else -# define HIGH_ADDR (void *)(-page_size) +# define HIGH_ADDR ((void *)(-page_size)) #endif +#define TEMPFILE "mmapfile" + static long page_size; +static int fd; -static void setup(void); -static void cleanup(void); - -int main(int ac, char **av) +static void run(void) { - int lc, fd; - void *addr; + fd = SAFE_OPEN(TEMPFILE, O_RDWR | O_CREAT, 0666); -#ifdef TST_ABI32 - tst_brkm(TCONF, NULL, "This test is only for 64bit"); -#endif + TESTPTR(mmap(HIGH_ADDR, page_size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0)); - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - fd = SAFE_OPEN(cleanup, "testfile", O_RDWR | O_CREAT, 0666); - - /* Attempt to mmap into highmem addr, should get ENOMEM */ - addr = mmap(HIGH_ADDR, page_size, PROT_READ, - MAP_SHARED | MAP_FIXED, fd, 0); - if (addr != MAP_FAILED) { - tst_resm(TFAIL, "mmap into high region " - "succeeded unexpectedly"); - munmap(addr, page_size); - close(fd); - continue; - } - - if (errno != ENOMEM && errno != EINVAL) { - tst_resm(TFAIL | TERRNO, "mmap into high region " - "failed unexpectedly"); - } else { - tst_resm(TPASS | TERRNO, "mmap into high region " - "failed as expected"); - } - - SAFE_CLOSE(cleanup, fd); + if (TST_RET_PTR != MAP_FAILED) { + tst_res(TFAIL, "mmap() into high mem region succeeded unexpectedly"); + SAFE_MUNMAP(TST_RET_PTR, page_size); + return; } - cleanup(); - tst_exit(); + if (TST_ERR == ENOMEM || TST_ERR == EINVAL) + tst_res(TPASS | TERRNO, "mmap() failed with expected errno"); + else + tst_res(TFAIL | TERRNO, "mmap() failed with unexpected errno"); + + SAFE_CLOSE(fd); } static void setup(void) { - tst_require_root(); - - tst_tmpdir(); - page_size = getpagesize(); - - TEST_PAUSE; } static void cleanup(void) { - tst_rmdir(); + if (fd > 0) + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .needs_tmpdir = 1 +}; diff --git a/testcases/kernel/syscalls/mmap/mmap16.c b/testcases/kernel/syscalls/mmap/mmap16.c index 4e0d8a3f..2b8b36c5 100755 --- a/testcases/kernel/syscalls/mmap/mmap16.c +++ b/testcases/kernel/syscalls/mmap/mmap16.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This is a regression test for a silent data corruption for a mmaped file * when filesystem gets out of space. * @@ -176,19 +174,15 @@ static struct tst_test test = { .needs_checkpoints = 1, .mount_device = 1, .mntpoint = MNTPOINT, - .dev_fs_type = "ext4", - .dev_fs_opts = (const char *const[]){ - "-b", - "1024", - NULL, - }, - .dev_extra_opts = (const char *const[]){ - "10240", - NULL, - }, - .needs_cmds = (const char *const[]){ - "mkfs.ext4", - NULL, + .filesystems = (struct tst_fs []) { + { + .type = "ext4", + .mkfs_opts = (const char *const[]){ + "-b", "1024", NULL + }, + .mkfs_size_opt = "10240", + }, + {} }, .tags = (const struct tst_tag[]){ {"linux-git", "d6320cbfc929"}, diff --git a/testcases/kernel/syscalls/mmap/mmap17.c b/testcases/kernel/syscalls/mmap/mmap17.c index 39703fbd..84e738d6 100755 --- a/testcases/kernel/syscalls/mmap/mmap17.c +++ b/testcases/kernel/syscalls/mmap/mmap17.c @@ -1,78 +1,66 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Zilogic Systems Pvt. Ltd., 2020 - * Email: code@zilogic.com + * Email: code@zilogic.com + * Copyright (C) 2025 SUSE LLC Andrea Cervesato */ -/* - * Test mmap with MAP_FIXED_NOREPLACE flag - * - * We are testing the MAP_FIXED_NOREPLACE flag of mmap() syscall. To check +/*\ + * Verify MAP_FIXED_NOREPLACE flag for the mmap() syscall and check * if an attempt to mmap at an exisiting mapping fails with EEXIST. - * The code allocates a free address by passing NULL to first mmap call - * Then tries to mmap with the same address using MAP_FIXED_NOREPLACE flag - * and the mapping fails as expected. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "lapi/mmap.h" #include "tst_test.h" +#include "lapi/mmap.h" static int fd_file1; static int fd_file2; static void *mapped_address; -static const char str[] = "Writing to mapped file"; +static const char msg[] = "Writing to mapped file"; +static int msg_len; #define FNAME1 "file1_to_mmap" #define FNAME2 "file2_to_mmap" static void setup(void) { + msg_len = strlen(msg); + fd_file1 = SAFE_OPEN(FNAME1, O_CREAT | O_RDWR, 0600); fd_file2 = SAFE_OPEN(FNAME2, O_CREAT | O_RDWR, 0600); + + SAFE_WRITE(SAFE_WRITE_ALL, fd_file1, msg, msg_len); + SAFE_WRITE(SAFE_WRITE_ALL, fd_file2, msg, msg_len); + + mapped_address = SAFE_MMAP(NULL, msg_len, + PROT_WRITE, MAP_PRIVATE, fd_file1, 0); } static void cleanup(void) { - int str_len; - - str_len = strlen(str); - if (fd_file2 > 0) SAFE_CLOSE(fd_file2); if (fd_file1 > 0) SAFE_CLOSE(fd_file1); if (mapped_address) - SAFE_MUNMAP(mapped_address, str_len); + SAFE_MUNMAP(mapped_address, msg_len); } static void test_mmap(void) { - int str_len; void *address; - str_len = strlen(str); - - SAFE_WRITE(SAFE_WRITE_ALL, fd_file1, str, str_len); - mapped_address = SAFE_MMAP(NULL, str_len, PROT_WRITE, - MAP_PRIVATE, fd_file1, 0); - - SAFE_WRITE(SAFE_WRITE_ALL, fd_file2, str, str_len); - - address = mmap(mapped_address, str_len, PROT_WRITE, + address = mmap(mapped_address, msg_len, PROT_WRITE, MAP_PRIVATE | MAP_FIXED_NOREPLACE, fd_file2, 0); + if (address == MAP_FAILED && errno == EEXIST) tst_res(TPASS, "mmap set errno to EEXIST as expected"); else tst_res(TFAIL | TERRNO, "mmap failed, with unexpected error " "code, expected EEXIST"); + + if (address != MAP_FAILED) + SAFE_MUNMAP(address, msg_len); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/mmap/mmap18.c b/testcases/kernel/syscalls/mmap/mmap18.c index b37b2989..5f0eee6b 100755 --- a/testcases/kernel/syscalls/mmap/mmap18.c +++ b/testcases/kernel/syscalls/mmap/mmap18.c @@ -1,31 +1,34 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Zilogic Systems Pvt. Ltd., 2020 - * Email: code@zilogic.com + * Email: code@zilogic.com + * Copyright (C) 2025 SUSE LLC Andrea Cervesato */ -/* - * Test mmap() MAP_GROWSDOWN flag +/*\ + * Verify mmap() syscall using MAP_GROWSDOWN flag. * - * # Test1: + * [Algorithm] * - * We assign the memory region partially allocated with MAP_GROWSDOWN flag to - * a thread as a stack and expect the mapping to grow when we touch the - * guard page by calling a recusive function in the thread that uses the - * growable mapping as a stack. + * **Test 1** * - * The kernel only grows the memory region when the stack pointer is within - * guard page when the guard page is touched so simply faulting the guard - * page will not cause the mapping to grow. + * We assign the memory region partially allocated with MAP_GROWSDOWN flag to + * a thread as a stack and expect the mapping to grow when we touch the + * guard page by calling a recusive function in the thread that uses the + * growable mapping as a stack. * - * Newer kernels does not allow a MAP_GROWSDOWN mapping to grow closer than - * 'stack_guard_gap' pages to an existing mapping. So when we map the stack we - * make sure there is enough of free address space before the lowest stack - * address. + * The kernel only grows the memory region when the stack pointer is within + * guard page when the guard page is touched so simply faulting the guard + * page will not cause the mapping to grow. * - * Kernel default 'stack_guard_gap' size is '256 * getpagesize()'. + * Newer kernels does not allow a MAP_GROWSDOWN mapping to grow closer than + * 'stack_guard_gap' pages to an existing mapping. So when we map the stack we + * make sure there is enough of free address space before the lowest stack + * address. * - * The stack memory map would look like: + * Kernel default `stack_guard_gap` size is `256 * getpagesize()`. + * + * The stack memory map would look like:: * * | - - - reserved size - - - | * @@ -38,21 +41,14 @@ * ^ ^ * stack bottom stack top * - * # Test2: + * **Test 2** * - * We allocate stack as we do in the first test but we mmap a page in the - * space the stack is supposed to grow into and we expect the thread to - * segfault when the guard page is faulted. + * We allocate stack as we do in the first test but we mmap a page in the + * space the stack is supposed to grow into and we expect the thread to + * segfault when the guard page is faulted. */ -#include #include -#include -#include -#include -#include -#include - #include "tst_test.h" #include "tst_safe_pthread.h" @@ -63,12 +59,12 @@ static bool __attribute__((noinline)) check_stackgrow_up(void) char local_var; static char *addr; - if (!addr) { - addr = &local_var; - return check_stackgrow_up(); - } + if (!addr) { + addr = &local_var; + return check_stackgrow_up(); + } - return (addr < &local_var); + return (addr < &local_var); } static void setup(void) @@ -90,7 +86,7 @@ static void *allocate_stack(size_t stack_size, size_t mapped_size) long reserved_size = 256 * page_size + stack_size; start = SAFE_MMAP(NULL, reserved_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); SAFE_MUNMAP(start, reserved_size); SAFE_MMAP((start + reserved_size - mapped_size), mapped_size, PROT_READ | PROT_WRITE, @@ -103,7 +99,7 @@ static void *allocate_stack(size_t stack_size, size_t mapped_size) tst_res(TINFO, "start = %p, stack_top = %p, stack bottom = %p", start, stack_top, stack_bottom); tst_res(TINFO, "mapped pages %zu, stack pages %zu", - mapped_size/page_size, stack_size/page_size); + mapped_size/page_size, stack_size/page_size); return stack_bottom; } @@ -192,10 +188,10 @@ static void grow_stack_fail(size_t stack_size, size_t mapped_size) } SAFE_WAIT(&wstatus); - if (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGSEGV) + if (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGSEGV) tst_res(TPASS, "Child killed by %s as expected", tst_strsig(SIGSEGV)); - else - tst_res(TFAIL, "Child: %s", tst_strstatus(wstatus)); + else + tst_res(TFAIL, "Child: %s", tst_strstatus(wstatus)); } static void run_test(void) diff --git a/testcases/kernel/syscalls/mmap/mmap19.c b/testcases/kernel/syscalls/mmap/mmap19.c index 90b3f45b..77ff771a 100755 --- a/testcases/kernel/syscalls/mmap/mmap19.c +++ b/testcases/kernel/syscalls/mmap/mmap19.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * If the kernel fails to correctly flush the TLB entry, the second mmap * will not show the correct data. * @@ -24,7 +22,7 @@ #define LEN 64 static int f1 = -1, f2 = -1; -static char *mm1 = NULL, *mm2 = NULL; +static char *mm1, *mm2; static const char tmp1[] = "testfile1"; static const char tmp2[] = "testfile2"; diff --git a/testcases/kernel/syscalls/mmap/mmap20.c b/testcases/kernel/syscalls/mmap/mmap20.c index 02d150e4..f8e5129d 100644 --- a/testcases/kernel/syscalls/mmap/mmap20.c +++ b/testcases/kernel/syscalls/mmap/mmap20.c @@ -4,17 +4,12 @@ */ /*\ - * [Description] - * * Test mmap(2) with MAP_SHARED_VALIDATE flag. * * Test expected EOPNOTSUPP errno when testing mmap(2) with MAP_SHARED_VALIDATE * flag and invalid flag. */ -#include -#include -#include #include "tst_test.h" #include "lapi/mmap.h" diff --git a/testcases/kernel/syscalls/mmap/mmap21.c b/testcases/kernel/syscalls/mmap/mmap21.c new file mode 100644 index 00000000..ea3ae835 --- /dev/null +++ b/testcases/kernel/syscalls/mmap/mmap21.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2000 Juan Quintela + * Aaron Laffin + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that we can use mmap() to map a large file, write to it via memory + * access, and read back the data from the file. + */ + +#include "tst_test.h" + +#define FILE_NAME "testfile" + +static char *str_pages; +static long long pages = 1000; +static long long memory_size; +static char *memory_data; +static char *buff; + +static void run(void) +{ + int fd; + pid_t pid; + + tst_res(TINFO, "mmap()ing file of %llu bytes", memory_size); + + fd = SAFE_OPEN(FILE_NAME, O_RDWR | O_CREAT, 0666); + SAFE_LSEEK(fd, memory_size, SEEK_SET); + SAFE_WRITE(SAFE_WRITE_ALL, fd, "\0", 1); + + memory_data = SAFE_MMAP(0, memory_size, PROT_WRITE, MAP_SHARED, fd, 0); + + pid = SAFE_FORK(); + if (!pid) { + tst_res(TINFO, "Touching mapped memory"); + + for (int i = 0; i < memory_size; i++) + memory_data[i] = (char)i; + + exit(0); + } + + tst_reap_children(); + + SAFE_MSYNC(memory_data, memory_size, MS_SYNC); + + memset(buff, 0, memory_size); + + SAFE_LSEEK(fd, 0, SEEK_SET); + SAFE_READ(0, fd, buff, memory_size); + SAFE_CLOSE(fd); + + for (int i = 0; i < memory_size; i++) { + if (buff[i] != (char)i) { + tst_res(TFAIL, "Mapped file has not been updated at byte %d", i); + goto exit; + } + } + + tst_res(TPASS, "Mapped file has been updated"); + +exit: + SAFE_MUNMAP(memory_data, memory_size); + memory_data = NULL; + + SAFE_UNLINK(FILE_NAME); +} + +static void setup(void) +{ + if (tst_parse_filesize(str_pages, &pages, 1, LLONG_MAX)) + tst_brk(TBROK, "Invalid number of pages: %s", str_pages); + + memory_size = pages * getpagesize(); + + buff = SAFE_MALLOC(memory_size); +} + +static void cleanup(void) +{ + if (buff) + free(buff); + + if (memory_data) + SAFE_MUNMAP(memory_data, memory_size); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .forks_child = 1, + .options = (struct tst_option[]) { + {"m:", &str_pages, "Number of pages (default 1000)"}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/mmap/mmap22.c b/testcases/kernel/syscalls/mmap/mmap22.c new file mode 100644 index 00000000..acff0851 --- /dev/null +++ b/testcases/kernel/syscalls/mmap/mmap22.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Wei Gao + */ + +/*\ + * [Description] + * + * Test mmap(2) with MAP_DROPPABLE flag. + * + * Test base on kernel selftests/mm/droppable.c + * + * Ensure that memory allocated with MAP_DROPPABLE can be reclaimed + * under memory pressure within a cgroup. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "tst_test.h" +#include "lapi/mmap.h" +#include "tst_safe_macros.h" + +#define MEM_LIMIT (256 * TST_MB) +#define ALLOC_SIZE (128 * TST_MB) + +static struct tst_cg_group *cg_child; +static pid_t child; +static int page_size; + +static void stress_child(void) +{ + for (;;) { + char *buf = malloc(page_size); + + if (!buf) + exit(1); + memset(buf, 'B', page_size); + } +} + +static void test_mmap(void) +{ + char *alloc; + unsigned char *vec; + size_t npages = ALLOC_SIZE / page_size; + + cg_child = tst_cg_group_mk(tst_cg, "child"); + SAFE_CG_PRINTF(tst_cg, "memory.max", "%d", MEM_LIMIT); + if (!TST_CG_VER_IS_V1(tst_cg, "memory")) + SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%d", 0); + else + SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%d", MEM_LIMIT); + SAFE_CG_PRINTF(cg_child, "cgroup.procs", "%d", getpid()); + + alloc = SAFE_MMAP(0, ALLOC_SIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); + + memset(alloc, 'A', ALLOC_SIZE); + + vec = SAFE_MALLOC(npages); + + child = SAFE_FORK(); + if (!child) + stress_child(); + + for (;;) { + if (!tst_remaining_runtime()) { + tst_res(TFAIL, "MAP_DROPPABLE did not drop pages within timeout"); + goto cleanup; + } + + SAFE_MINCORE(alloc, ALLOC_SIZE, vec); + + for (size_t i = 0; i < npages; i++) { + if (!(vec[i] & 1)) { + tst_res(TPASS, "MAP_DROPPABLE page reclaimed by kernel"); + goto cleanup; + } + } + + usleep(100000); + } + +cleanup: + if (child > 0) { + SAFE_KILL(child, SIGKILL); + SAFE_WAITPID(child, NULL, 0); + } + SAFE_MUNMAP(alloc, ALLOC_SIZE); + free(vec); + SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid()); + cg_child = tst_cg_group_rm(cg_child); +} + +static void setup(void) +{ + void *addr = mmap(0, 1, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); + + if (addr == MAP_FAILED && errno == EINVAL) + tst_brk(TCONF, "MAP_DROPPABLE not supported"); + + if (addr == MAP_FAILED) + tst_brk(TBROK | TERRNO, "mmap() MAP_DROPPABLE failed"); + + SAFE_MUNMAP(addr, 1); + page_size = getpagesize(); +} + +static void cleanup(void) +{ + if (cg_child) { + SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid()); + cg_child = tst_cg_group_rm(cg_child); + } +} + +static struct tst_test test = { + .test_all = test_mmap, + .needs_tmpdir = 1, + .forks_child = 1, + .needs_cgroup_ctrls = (const char *const []){ "memory", NULL }, + .needs_root = 1, + .cleanup = cleanup, + .setup = setup, + .runtime = 30, + .min_mem_avail = 300, +}; diff --git a/testcases/kernel/syscalls/modify_ldt/.gitignore b/testcases/kernel/syscalls/modify_ldt/.gitignore index c0b8bbf5..c8817b2c 100755 --- a/testcases/kernel/syscalls/modify_ldt/.gitignore +++ b/testcases/kernel/syscalls/modify_ldt/.gitignore @@ -1,3 +1,2 @@ -/modify_ldt01 -/modify_ldt02 -/modify_ldt03 +modify_ldt01 +modify_ldt02 diff --git a/testcases/kernel/syscalls/modify_ldt/common.h b/testcases/kernel/syscalls/modify_ldt/common.h new file mode 100644 index 00000000..3f92b677 --- /dev/null +++ b/testcases/kernel/syscalls/modify_ldt/common.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +#ifndef COMMON_H +#define COMMON_H + +#include "tst_test.h" +#include "lapi/ldt.h" + +static inline void create_segment(void *seg, size_t size) +{ + struct user_desc entry = { + .entry_number = 0, + .base_addr = (unsigned long)seg, + .limit = size, + .seg_32bit = 1, + .contents = 0, + .read_exec_only = 0, + .limit_in_pages = 0, + .seg_not_present = 0, + }; + + SAFE_MODIFY_LDT(1, &entry, sizeof(entry)); +} + +#endif diff --git a/testcases/kernel/syscalls/modify_ldt/modify_ldt01.c b/testcases/kernel/syscalls/modify_ldt/modify_ldt01.c index 684e5377..3ae1a558 100755 --- a/testcases/kernel/syscalls/modify_ldt/modify_ldt01.c +++ b/testcases/kernel/syscalls/modify_ldt/modify_ldt01.c @@ -1,230 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ - -/* - * NAME - * modify_ldt01.c - * - * DESCRIPTION - * Testcase to check the error conditions for modify_ldt(2) - * - * CALLS - * modify_ldt() - * - * ALGORITHM - * block1: - * Invoke modify_ldt() with a func value which is neither - * 0 or 1. Verify that ENOSYS is set. - * block2: - * Invoke mprotect() with ptr == NULL. Verify that EINVAL - * is set. - * block3: - * Create an LDT segment. - * Try to read from an invalid pointer. - * Verify that EFAULT is set. - * - * USAGE - * modify_ldt01 - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that modify_ldt() calls: * - * RESTRICTIONS - * None + * - Fails with EFAULT, when reading (func=0) from an invalid pointer + * - Passes when reading (func=0) from a valid pointer + * - Fails with EINVAL, when writing (func=1) to an invalid pointer + * - Fails with EINVAL, when writing (func=1) with an invalid bytecount value + * - Fails with EINVAL, when writing (func=1) an entry with invalid values + * - Fails with EINVAL, when writing (func=0x11) an entry with invalid values */ -#include "config.h" -#include "test.h" +#include "tst_test.h" -TCID_DEFINE(modify_ldt01); -int TST_TOTAL = 1; - -#if defined(__i386__) && defined(HAVE_MODIFY_LDT) - -#ifdef HAVE_ASM_LDT_H -#include -#endif -extern int modify_ldt(int, void *, unsigned long); - -#include -#include - -/* Newer ldt.h files use user_desc, instead of modify_ldt_ldt_s */ -#ifdef HAVE_STRUCT_USER_DESC -typedef struct user_desc modify_ldt_s; -#elif HAVE_STRUCT_MODIFY_LDT_LDT_S -typedef struct modify_ldt_ldt_s modify_ldt_s; -#else -typedef struct modify_ldt_ldt_t { - unsigned int entry_number; - unsigned long int base_addr; - unsigned int limit; - unsigned int seg_32bit:1; - unsigned int contents:2; - unsigned int read_exec_only:1; - unsigned int limit_in_pages:1; - unsigned int seg_not_present:1; - unsigned int useable:1; - unsigned int empty:25; -} modify_ldt_s; -#endif - -int create_segment(void *, size_t); -void cleanup(void); -void setup(void); - -int main(int ac, char **av) -{ - int lc; +#ifdef __i386__ +#include "common.h" +static void *ptr; +static char *buf; +static struct user_desc invalid_entry; +static struct tcase { + int tfunc; void *ptr; - int retval, func; + unsigned long bytecount; + int exp_errno; +} tcases[] = { + { 0, &ptr, sizeof(ptr), EFAULT }, + { 0, &buf, sizeof(buf), 0 }, + { 1, (void *)0, 0, EINVAL }, + { 1, &buf, sizeof(struct user_desc) - 1, EINVAL }, + { 1, &invalid_entry, sizeof(struct user_desc), EINVAL }, + { 0x11, &invalid_entry, sizeof(struct user_desc), EINVAL }, +}; - int seg[4]; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - /* reset tst_count in case we are looping */ - tst_count = 0; - - /* - * Check for ENOSYS. - */ - ptr = malloc(10); - func = 100; - retval = modify_ldt(func, ptr, sizeof(ptr)); - if (retval < 0) { - if (errno != ENOSYS) { - tst_resm(TFAIL, "modify_ldt() set invalid " - "errno, expected ENOSYS, got: %d", - errno); - } else { - tst_resm(TPASS, - "modify_ldt() set expected errno"); - } - } else { - tst_resm(TFAIL, "modify_ldt error: " - "unexpected return value %d", retval); - } - - free(ptr); - - /* - * Check for EINVAL - */ - ptr = 0; - - retval = modify_ldt(1, ptr, sizeof(ptr)); - if (retval < 0) { - if (errno != EINVAL) { - tst_resm(TFAIL, "modify_ldt() set invalid " - "errno, expected EINVAL, got: %d", - errno); - } else { - tst_resm(TPASS, - "modify_ldt() set expected errno"); - } - } else { - tst_resm(TFAIL, "modify_ldt error: " - "unexpected return value %d", retval); - } - - /* - * Create a new LDT segment. - */ - if (create_segment(seg, sizeof(seg)) == -1) { - tst_brkm(TBROK, cleanup, "Creation of segment failed"); - } - - /* - * Check for EFAULT - */ - ptr = sbrk(0); - - retval = modify_ldt(0, ptr + 0xFFF, sizeof(ptr)); - if (retval < 0) { - if (errno != EFAULT) { - tst_resm(TFAIL, "modify_ldt() set invalid " - "errno, expected EFAULT, got: %d", - errno); - } else { - tst_resm(TPASS, - "modify_ldt() set expected errno"); - } - } else { - tst_resm(TFAIL, "modify_ldt error: " - "unexpected return value %d", retval); - } - } - cleanup(); - tst_exit(); -} - -/* - * create_segment() - - */ -int create_segment(void *seg, size_t size) +void run(unsigned int i) { - modify_ldt_s entry; + struct tcase *tc = &tcases[i]; - entry.entry_number = 0; - entry.base_addr = (unsigned long)seg; - entry.limit = size; - entry.seg_32bit = 1; - entry.contents = 0; - entry.read_exec_only = 0; - entry.limit_in_pages = 0; - entry.seg_not_present = 0; - - return modify_ldt(1, &entry, sizeof(entry)); + if (tc->exp_errno) + TST_EXP_FAIL(modify_ldt(tc->tfunc, tc->ptr, tc->bytecount), + tc->exp_errno); + else + TST_EXP_POSITIVE(modify_ldt(tc->tfunc, tc->ptr, tc->bytecount)); } void setup(void) { + int seg[4]; - tst_sig(FORK, DEF_HANDLER, cleanup); + create_segment(seg, sizeof(seg)); - TEST_PAUSE; + invalid_entry.contents = 3; + invalid_entry.seg_not_present = 0; + + ptr = sbrk(0) + 0xFFF; + tcases[0].ptr = ptr; } +#endif /* __i386__ */ -void cleanup(void) -{ - -} - -#elif HAVE_MODIFY_LDT -int main(void) -{ - tst_brkm(TCONF, - NULL, - "modify_ldt is available but not tested on the platform than __i386__"); -} - -#else -int main(void) -{ - tst_resm(TINFO, "modify_ldt01 test only for ix86"); - tst_exit(); -} - -#endif /* defined(__i386__) */ +static struct tst_test test = { +#ifdef __i386__ + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .bufs = + (struct tst_buffers[]){ + { &buf, .size = sizeof(struct user_desc) }, + {}, + }, +#endif /* __i386__ */ + .supported_archs = (const char *[]){"x86", NULL}, +}; diff --git a/testcases/kernel/syscalls/modify_ldt/modify_ldt02.c b/testcases/kernel/syscalls/modify_ldt/modify_ldt02.c index f911aa5f..b3e1bc42 100755 --- a/testcases/kernel/syscalls/modify_ldt/modify_ldt02.c +++ b/testcases/kernel/syscalls/modify_ldt/modify_ldt02.c @@ -1,199 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ - -/* - * NAME - * modify_ldt02.c - * - * DESCRIPTION - * Testcase to check the error conditions for modify_ldt(2) - * - * ALGORITHM - * block1: - * Create a segment at entry 0 and a valid base address. - * Read the contents of the segment thru' fs register. - * Validate the data. - * Write an invalid base address into entry 0. - * Read the contents of entry 0 in the child process. - * Verify that a SIGSEGV is incurred. - * - * USAGE - * modify_ldt02 - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * None + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -#include "config.h" -#include "test.h" +/*\ + * Verify that after writing an invalid base address into a segment entry, + * a subsequent segment entry read will raise SIGSEV. + */ -TCID_DEFINE(modify_ldt02); -int TST_TOTAL = 1; +#include "tst_test.h" -#if defined(__i386__) && defined(HAVE_MODIFY_LDT) - -#ifdef HAVE_ASM_LDT_H -#include -#endif -extern int modify_ldt(int, void *, unsigned long); - -#include -#include -#include -#include - -/* Newer ldt.h files use user_desc, instead of modify_ldt_ldt_s */ -#ifdef HAVE_STRUCT_USER_DESC -typedef struct user_desc modify_ldt_s; -#elif HAVE_STRUCT_MODIFY_LDT_LDT_S -typedef struct modify_ldt_ldt_s modify_ldt_s; -#else -typedef struct modify_ldt_ldt_t { - unsigned int entry_number; - unsigned long int base_addr; - unsigned int limit; - unsigned int seg_32bit:1; - unsigned int contents:2; - unsigned int read_exec_only:1; - unsigned int limit_in_pages:1; - unsigned int seg_not_present:1; - unsigned int useable:1; - unsigned int empty:25; -} modify_ldt_s; -#endif - -int create_segment(void *, size_t); -int read_segment(unsigned int); -void cleanup(void); -void setup(void); - -int main(int ac, char **av) -{ - int lc; - - int val, pid, status; - - int seg[4]; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - seg[0] = 12345; - if (create_segment(seg, sizeof(seg)) == -1) { - tst_brkm(TBROK, cleanup, "Creation of segment failed"); - } - - val = read_segment(0); - - if (val != seg[0]) { - tst_resm(TFAIL, "Invalid value read %d, expected %d", - val, seg[0]); - } else - tst_resm(TPASS, "value read as expected"); - - if (create_segment(0, 10) == -1) { - tst_brkm(TBROK, cleanup, "Creation of segment failed"); - } - - tst_old_flush(); - if ((pid = FORK_OR_VFORK()) == 0) { - signal(SIGSEGV, SIG_DFL); - val = read_segment(0); - exit(1); - } - - (void)waitpid(pid, &status, 0); - - if (WEXITSTATUS(status) != 0) { - tst_resm(TFAIL, "Did not generate SEGV, child returned " - "unexpected status"); - } else { - if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV)) - tst_resm(TPASS, "generate SEGV as expected"); - else - tst_resm(TFAIL, "Did not generate SEGV"); - } - } - cleanup(); - tst_exit(); - -} - -int create_segment(void *seg, size_t size) -{ - modify_ldt_s entry; - - entry.entry_number = 0; - entry.base_addr = (unsigned long)seg; - entry.limit = size; - entry.seg_32bit = 1; - entry.contents = 0; - entry.read_exec_only = 0; - entry.limit_in_pages = 0; - entry.seg_not_present = 0; - - return modify_ldt(1, &entry, sizeof(entry)); -} +#ifdef __i386__ +#include "common.h" int read_segment(unsigned int index) { int res; + __asm__ __volatile__("\n\ push $0x0007;\n\ pop %%fs;\n\ - movl %%fs:(%1), %0":"=r"(res) - :"r"(index * sizeof(int))); + movl %%fs:(%1), %0" + : "=r"(res) + : "r"(index * sizeof(int))); + return res; } -void setup(void) +void run(void) { - tst_sig(FORK, DEF_HANDLER, cleanup); + int pid, status, seg[4]; - TEST_PAUSE; + seg[0] = 12345; + + create_segment(seg, sizeof(seg)); + + TST_EXP_EQ_LI(read_segment(0), seg[0]); + + create_segment(0, 10); + + pid = SAFE_FORK(); + if (!pid) { + read_segment(0); + exit(1); + } + + SAFE_WAITPID(pid, &status, 0); + if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV)) + tst_res(TPASS, "generate SEGV as expected"); + else + tst_res(TFAIL, "child %s", tst_strstatus(status)); } +#endif /* __i386__ */ -void cleanup(void) -{ - -} -#elif HAVE_MODIFY_LDT -int main(void) -{ - tst_brkm(TCONF, - NULL, - "modify_ldt is available but not tested on the platform than __i386__"); -} - -#else /* if defined(__i386__) */ - -int main(void) -{ - tst_resm(TINFO, "modify_ldt02 test only for ix86"); - tst_exit(); -} - -#endif /* if defined(__i386__) */ +static struct tst_test test = { +#ifdef __i386__ + .test_all = run, +#endif /* __i386__ */ + .supported_archs = (const char *[]){"x86", NULL}, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/modify_ldt/modify_ldt03.c b/testcases/kernel/syscalls/modify_ldt/modify_ldt03.c deleted file mode 100755 index 01730e0e..00000000 --- a/testcases/kernel/syscalls/modify_ldt/modify_ldt03.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2014 Fujitsu Ltd. - * Author: Zeng Linggang - * - * 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. - */ -/* - * DESCRIPTION - * Basic test for modify_ldt(2) using func=0 argument. - */ - -#include "config.h" -#include "test.h" - -char *TCID = "modify_ldt03"; -int TST_TOTAL = 1; - -#if defined(__i386__) && defined(HAVE_MODIFY_LDT) - -#ifdef HAVE_ASM_LDT_H -# include -#endif -extern int modify_ldt(int, void *, unsigned long); - -#include -#include -#include -#include -#include "safe_macros.h" - -#ifdef HAVE_STRUCT_USER_DESC -# define SIZE sizeof(struct user_desc) -#else -# define SIZE 16 -#endif - -static char buf[SIZE]; -static void cleanup(void); -static void setup(void); - -int main(int ac, char **av) -{ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - TEST(modify_ldt(0, buf, SIZE)); - - if (TEST_RETURN < 0) { - tst_resm(TFAIL | TTERRNO, - "modify_ldt() failed with errno: %s", - strerror(TEST_ERRNO)); - } else { - tst_resm(TPASS, "modify_ldt() tested success"); - } - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - -static void cleanup(void) -{ -} - -#elif HAVE_MODIFY_LDT - -int main(void) -{ - tst_brkm(TCONF, - NULL, "modify_ldt is available but not tested on the platform than " - "__i386__"); -} - -#else /* if defined(__i386__) */ - -int main(void) -{ - tst_resm(TINFO, "modify_ldt03 test only for ix86"); - tst_exit(); -} - -#endif /* if defined(__i386__) */ diff --git a/testcases/kernel/syscalls/mount/.gitignore b/testcases/kernel/syscalls/mount/.gitignore index 80885dbf..3eee5863 100755 --- a/testcases/kernel/syscalls/mount/.gitignore +++ b/testcases/kernel/syscalls/mount/.gitignore @@ -6,3 +6,4 @@ /mount05 /mount06 /mount07 +/mount08 diff --git a/testcases/kernel/syscalls/mount/mount01.c b/testcases/kernel/syscalls/mount/mount01.c index 1d902ba8..a513e0fb 100755 --- a/testcases/kernel/syscalls/mount/mount01.c +++ b/testcases/kernel/syscalls/mount/mount01.c @@ -1,99 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. 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. - * - * 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. - * - * AUTHOR : Nirmala Devi Dhanasekar - * - * DESCRIPTION - * This is a Phase I test for the mount(2) system call. - * It is intended to provide a limited exposure of the system call. + * Nirmala Devi Dhanasekar + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -#include +/*\ + * Basic test that checks mount() syscall works on multiple filesystems. + */ + +#include "tst_test.h" #include -#include -#include -#include "test.h" -#include "safe_macros.h" -static void setup(void); -static void cleanup(void); - -char *TCID = "mount01"; -int TST_TOTAL = 1; - -#define DIR_MODE (S_IRWXU | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP) #define MNTPOINT "mntpoint" -static const char *device; -static const char *fs_type; - -int main(int ac, char **av) -{ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - TEST(mount(device, MNTPOINT, fs_type, 0, NULL)); - - if (TEST_RETURN != 0) { - tst_resm(TFAIL | TTERRNO, "mount(2) failed"); - } else { - tst_resm(TPASS, "mount(2) passed "); - TEST(tst_umount(MNTPOINT)); - if (TEST_RETURN != 0) { - tst_brkm(TBROK | TTERRNO, cleanup, - "umount(2) failed"); - } - } - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - tst_require_root(); - - tst_tmpdir(); - - fs_type = tst_dev_fs_type(); - device = tst_acquire_device(cleanup); - - if (!device) - tst_brkm(TCONF, cleanup, "Failed to obtain block device"); - - tst_mkfs(cleanup, device, fs_type, NULL, NULL); - - SAFE_MKDIR(cleanup, MNTPOINT, DIR_MODE); - - TEST_PAUSE; -} - static void cleanup(void) { - if (device) - tst_release_device(device); - - tst_rmdir(); + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); } + +static void run(void) +{ + TST_EXP_PASS(mount(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL)); + + if (tst_is_mounted(MNTPOINT)) { + tst_res(TPASS, "folder has been mounted"); + SAFE_UMOUNT(MNTPOINT); + } else { + tst_res(TFAIL, "folder has not been mounted"); + } +} + +static struct tst_test test = { + .cleanup = cleanup, + .test_all = run, + .needs_root = 1, + .format_device = 1, + .all_filesystems = 1, + .mntpoint = MNTPOINT, + .skip_filesystems = (const char *const []){ + "ntfs", + NULL + }, +}; diff --git a/testcases/kernel/syscalls/mount/mount02.c b/testcases/kernel/syscalls/mount/mount02.c index 392b4fd3..734c93b7 100755 --- a/testcases/kernel/syscalls/mount/mount02.c +++ b/testcases/kernel/syscalls/mount/mount02.c @@ -1,212 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. - * AUTHOR: Nirmala Devi Dhanasekar - * Copyright (c) 2014 Cyril Hrubis - * - * 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. - * + * Nirmala Devi Dhanasekar + * Copyright (c) Linux Test Project, 2005-2023 + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -/* +/*\ + * Check for basic errors returned by mount(2) system call. + * + * - ENODEV if filesystem type not configured + * - ENOTBLK if specialfile is not a block device + * - EBUSY if specialfile is already mounted or it cannot be remounted + * read-only, because it still holds files open for writing. + * - EINVAL if specialfile or device is invalid or a remount was attempted, + * while source was not already mounted on target. + * - EFAULT if special file or device file points to invalid address space. + * - ENAMETOOLONG if pathname was longer than MAXPATHLEN. + * - ENOENT if pathname was empty or has a nonexistent component. + * - ENOTDIR if not a directory. + */ - DESCRIPTION - Check for basic errors returned by mount(2) system call. - - Verify that mount(2) returns -1 and sets errno to - 1) ENODEV if filesystem type not configured - 2) ENOTBLK if specialfile is not a block device - 3) EBUSY if specialfile is already mounted or - it cannot be remounted read-only, because it still holds - files open for writing. - 4) EINVAL if specialfile or device is invalid or - a remount was attempted, while source was not already - mounted on target. - 5) EFAULT if specialfile or device file points to invalid address space. - 6) ENAMETOOLONG if pathname was longer than MAXPATHLEN. - 7) ENOENT if pathname was empty or has a nonexistent component. - 8) ENOTDIR if not a directory. -*/ - -#include +#include "tst_test.h" #include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" -static void setup(void); -static void cleanup(void); - -char *TCID = "mount02"; - -#define DIR_MODE (S_IRWXU | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP) -#define FILE_MODE (S_IRWXU | S_IRWXG | S_IRWXO) +#define MNTPOINT "mntpoint" +#define TEST_FILE MNTPOINT"/file" static char path[PATH_MAX + 2]; static const char *long_path = path; -static const char *fs_type; -static const char *wrong_fs_type = "error"; -static const char *mntpoint = "mntpoint"; static const char *device; -static const char *null = NULL; -static const char *fault = (void*)-1; +static const char *fs_type; +static const char *null; +static const char *wrong_fs_type = "error"; +static const char *mntpoint = MNTPOINT; +static const char *fault; static const char *nonexistent = "nonexistent"; static const char *char_dev = "char_device"; static const char *file = "filename"; static int fd; -static void do_umount(void); -static void close_umount(void); -static void do_mount(void); -static void mount_open(void); +static void pre_mount(void); +static void post_umount(void); +static void pre_create_file(void); +static void post_delete_file(void); static struct test_case { const char **device; const char **mntpoint; const char **fs_type; + const char *desc; unsigned long flag; int exp_errno; void (*setup)(void); void (*cleanup)(void); -} tc[] = { - {&device, &mntpoint, &wrong_fs_type, 0, ENODEV, NULL, NULL}, - {&char_dev, &mntpoint, &fs_type, 0, ENOTBLK, NULL, NULL}, - {&device, &mntpoint, &fs_type, 0, EBUSY, do_mount, do_umount}, - {&device, &mntpoint, &fs_type, MS_REMOUNT | MS_RDONLY, EBUSY, - mount_open, close_umount}, - {&null, &mntpoint, &fs_type, 0, EINVAL, NULL, NULL}, - {&device, &mntpoint, &null, 0, EINVAL, NULL, NULL}, - {&device, &mntpoint, &fs_type, MS_REMOUNT, EINVAL, NULL, NULL}, - {&fault, &mntpoint, &fs_type, 0, EFAULT, NULL, NULL}, - {&device, &mntpoint, &fault, 0, EFAULT, NULL, NULL}, - {&device, &long_path, &fs_type, 0, ENAMETOOLONG, NULL, NULL}, - {&device, &nonexistent, &fs_type, 0, ENOENT, NULL, NULL}, - {&device, &file, &fs_type, 0, ENOTDIR, NULL, NULL}, +} test_cases[] = { + {.fs_type = &wrong_fs_type, .desc = "wrong FS type", .exp_errno = ENODEV}, + {.device = &char_dev, .desc = "char device", .exp_errno = ENOTBLK}, + {.desc = "mounted folder", .exp_errno = EBUSY, .setup = pre_mount, .cleanup = post_umount}, + {.desc = "mounted folder containing file", .flag = MS_REMOUNT | MS_RDONLY, + .exp_errno = EBUSY, .setup = pre_create_file, .cleanup = post_delete_file}, + {.device = &null, .desc = "invalid device", .exp_errno = EINVAL}, + {.fs_type = &null, .desc = "invalid device type", .exp_errno = EINVAL}, + {.desc = "mounted folder", .flag = MS_REMOUNT, .exp_errno = EINVAL}, + {.device = &fault, .desc = "fault device", .exp_errno = EFAULT}, + {.fs_type = &fault, .desc = "fault device type", .exp_errno = EFAULT}, + {.mntpoint = &long_path, .desc = "long name", .exp_errno = ENAMETOOLONG}, + {.mntpoint = &nonexistent, .desc = "non existant folder", .exp_errno = ENOENT}, + {.device = &device, .mntpoint = &file, .desc = "file", .exp_errno = ENOTDIR}, }; -int TST_TOTAL = ARRAY_SIZE(tc); - -static void verify_mount(struct test_case *tc) +static void pre_mount(void) { - if (tc->setup) - tc->setup(); - - TEST(mount(*tc->device, *tc->mntpoint, *tc->fs_type, tc->flag, NULL)); - - if (TEST_RETURN != -1) { - tst_resm(TFAIL, "mount() succeded unexpectedly (ret=%li)", - TEST_RETURN); - goto cleanup; - } - - if (TEST_ERRNO != tc->exp_errno) { - tst_resm(TFAIL | TTERRNO, - "mount() was expected to fail with %s(%i)", - tst_strerrno(tc->exp_errno), tc->exp_errno); - goto cleanup; - } - - tst_resm(TPASS | TTERRNO, "mount() failed expectedly"); - -cleanup: - if (tc->cleanup) - tc->cleanup(); + SAFE_MOUNT(device, mntpoint, fs_type, 0, NULL); } -int main(int ac, char **av) +static void post_umount(void) { - int lc, i; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; i < TST_TOTAL; ++i) - verify_mount(tc + i); - } - - cleanup(); - tst_exit(); + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); } -static void do_mount(void) +static void pre_create_file(void) { - if (mount(device, mntpoint, fs_type, 0, NULL)) - tst_brkm(TBROK | TERRNO, cleanup, "Failed to mount(mntpoint)"); + pre_mount(); + fd = SAFE_OPEN(TEST_FILE, O_CREAT | O_RDWR, 0700); } -static void mount_open(void) +static void post_delete_file(void) { - do_mount(); - - fd = SAFE_OPEN(cleanup, "mntpoint/file", O_CREAT | O_RDWR, S_IRWXU); -} - -static void close_umount(void) -{ - SAFE_CLOSE(cleanup, fd); - do_umount(); -} - -static void do_umount(void) -{ - if (tst_umount(mntpoint)) - tst_brkm(TBROK | TERRNO, cleanup, "Failed to umount(mntpoint)"); + SAFE_CLOSE(fd); + post_umount(); } static void setup(void) { - dev_t dev; + fault = tst_get_bad_addr(NULL); - tst_sig(FORK, DEF_HANDLER, cleanup); - - tst_require_root(); - - tst_tmpdir(); - - SAFE_TOUCH(cleanup, file, FILE_MODE, NULL); - - fs_type = tst_dev_fs_type(); - device = tst_acquire_device(cleanup); - - if (!device) - tst_brkm(TCONF, cleanup, "Failed to obtain block device"); - - tst_mkfs(cleanup, device, fs_type, NULL, NULL); - - SAFE_MKDIR(cleanup, mntpoint, DIR_MODE); + device = tst_device->dev; + fs_type = tst_device->fs_type; memset(path, 'a', PATH_MAX + 1); - dev = makedev(1, 3); - if (mknod(char_dev, S_IFCHR | FILE_MODE, dev)) { - tst_brkm(TBROK | TERRNO, cleanup, - "failed to mknod(char_dev, S_IFCHR | FILE_MODE, %lu)", - dev); - } - - TEST_PAUSE; + SAFE_MKNOD(char_dev, S_IFCHR | 0777, 0); + SAFE_TOUCH(file, 0777, 0); } static void cleanup(void) { - if (device) - tst_release_device(device); - - tst_rmdir(); + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); } + +static void run(unsigned int i) +{ + struct test_case *tc = &test_cases[i]; + + if (!tc->device) + tc->device = &device; + + if (!tc->mntpoint) + tc->mntpoint = &mntpoint; + + if (!tc->fs_type) + tc->fs_type = &fs_type; + + if (tc->setup) + tc->setup(); + + TST_EXP_FAIL(mount(*tc->device, *tc->mntpoint, *tc->fs_type, tc->flag, NULL), + tc->exp_errno, + "mounting %s", + tc->desc); + + if (tc->cleanup) + tc->cleanup(); +} + +static struct tst_test test = { + .tcnt = ARRAY_SIZE(test_cases), + .test = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .format_device = 1, + .mntpoint = MNTPOINT, +}; diff --git a/testcases/kernel/syscalls/mount/mount03.c b/testcases/kernel/syscalls/mount/mount03.c index 98d5933b..98be3dc0 100755 --- a/testcases/kernel/syscalls/mount/mount03.c +++ b/testcases/kernel/syscalls/mount/mount03.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Linux Test Project, 2022 * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. */ /*\ - * [Description] - * * Check mount(2) system call with various flags. * * Verify that mount(2) syscall passes for each flag setting and validate diff --git a/testcases/kernel/syscalls/mount/mount03_suid_child.c b/testcases/kernel/syscalls/mount/mount03_suid_child.c index 3b5cf5fe..40c800ef 100644 --- a/testcases/kernel/syscalls/mount/mount03_suid_child.c +++ b/testcases/kernel/syscalls/mount/mount03_suid_child.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Copyright (c) 2022 Petr Vorel diff --git a/testcases/kernel/syscalls/mount/mount04.c b/testcases/kernel/syscalls/mount/mount04.c index 5969fbac..fe0ceaef 100755 --- a/testcases/kernel/syscalls/mount/mount04.c +++ b/testcases/kernel/syscalls/mount/mount04.c @@ -1,119 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Wipro Technologies Ltd, 2002. 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. - * - * 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 - * Verify that mount(2) returns -1 and sets errno to EPERM if the user - * is not the super-user. + * Nirmala Devi Dhanasekar + * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ -#include -#include -#include -#include -#include +/*\ + * Verify that mount(2) returns -1 and sets errno to EPERM if the user + * is not root. + */ + +#include "tst_test.h" #include -#include "test.h" -#include "safe_macros.h" +#include -static void setup(void); -static void cleanup(void); - -char *TCID = "mount04"; - -#define DIR_MODE S_IRWXU | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP - -static char *mntpoint = "mntpoint"; -static const char *fs_type; -static const char *device; - -int TST_TOTAL = 1; - -static void verify_mount(void) -{ - - TEST(mount(device, mntpoint, fs_type, 0, NULL)); - - if (TEST_RETURN == -1) { - if (TEST_ERRNO == EPERM) - tst_resm(TPASS | TTERRNO, "mount() failed expectedly"); - else - tst_resm(TFAIL | TTERRNO, - "mount() was expected to fail with EPERM"); - return; - } - - if (TEST_RETURN == 0) { - tst_resm(TFAIL, "mount() succeeded unexpectedly"); - if (tst_umount(mntpoint)) - tst_brkm(TBROK, cleanup, "umount() failed"); - return; - } - - tst_resm(TFAIL | TTERRNO, "mount() returned %li", TEST_RETURN); -} - -int main(int ac, char **av) -{ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - verify_mount(); - } - - cleanup(); - tst_exit(); -} - -static void setup(void) -{ - char nobody_uid[] = "nobody"; - struct passwd *ltpuser; - - tst_sig(FORK, DEF_HANDLER, cleanup); - - tst_require_root(); - - tst_tmpdir(); - - fs_type = tst_dev_fs_type(); - device = tst_acquire_device(cleanup); - - if (!device) - tst_brkm(TCONF, cleanup, "Failed to obtain block device"); - - tst_mkfs(cleanup, device, fs_type, NULL, NULL); - - ltpuser = SAFE_GETPWNAM(cleanup, nobody_uid); - SAFE_SETEUID(cleanup, ltpuser->pw_uid); - SAFE_MKDIR(cleanup, mntpoint, DIR_MODE); - - TEST_PAUSE; -} +#define MNTPOINT "mntpoint" static void cleanup(void) { - if (seteuid(0)) - tst_resm(TWARN | TERRNO, "seteuid(0) failed"); - - if (device) - tst_release_device(device); - - tst_rmdir(); + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); } + +static void run(void) +{ + struct passwd *ltpuser; + + ltpuser = SAFE_GETPWNAM("nobody"); + SAFE_SETEUID(ltpuser->pw_uid); + + TST_EXP_FAIL( + mount(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL), + EPERM, + "mount() failed expectedly for normal user" + ); + + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); +} + +static struct tst_test test = { + .cleanup = cleanup, + .test_all = run, + .needs_root = 1, + .format_device = 1, + .mntpoint = MNTPOINT, +}; diff --git a/testcases/kernel/syscalls/mount/mount05.c b/testcases/kernel/syscalls/mount/mount05.c index ca26f973..5585e230 100755 --- a/testcases/kernel/syscalls/mount/mount05.c +++ b/testcases/kernel/syscalls/mount/mount05.c @@ -1,131 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2013 Fujitsu Ltd. - * Author: DAN LI - * - * 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. - * + * Copyright (c) 2013 Fujitsu Ltd. Dan Li + * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ -/* - * DESCRIPTION - * Test for feature MS_BIND of mount. - * "Perform a bind mount, making a file or a directory subtree visible - * at another point within a file system." +/*\ + * Test for feature MS_BIND of mount, which performs a bind mount, making a file + * or a directory subtree visible at another point within a file system. */ -#include +#include "tst_test.h" #include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#define MNTPOINT1 "mntpoint1" +#define TESTFILE1 MNTPOINT1 "/testfile" +#define TESTDIR1 MNTPOINT1 "/testdir" -static void help(void); -static void setup(void); -static void cleanup(void); +#define MNTPOINT2 "mntpoint2" +#define TESTFILE2 MNTPOINT2 "/testfile" +#define TESTDIR2 MNTPOINT2 "/testdir" -char *TCID = "mount05"; -int TST_TOTAL = 1; +static void setup(void) +{ + SAFE_MOUNT(tst_device->dev, MNTPOINT1, tst_device->fs_type, 0, NULL); -#define DIR_MODE (S_IRWXU | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP) + tst_res(TINFO, "Creating file in '%s'", TESTFILE1); -static int dflag; -static char *fstype = "ext2"; -static char *device; -static const char file_src[] = "mnt_src/tstfile"; -static const char file_des[] = "mnt_des/tstfile"; -static const char mntpoint_src[] = "mnt_src"; -static const char mntpoint_des[] = "mnt_des"; + SAFE_FILE_PRINTF(TESTFILE1, "LTP TEST FILE"); + SAFE_MKDIR(TESTDIR1, 0750); +} -static option_t options[] = { - {"T:", NULL, &fstype}, - {"D:", &dflag, &device}, - {NULL, NULL, NULL}, +static void cleanup(void) +{ + if (tst_is_mounted(MNTPOINT1)) + SAFE_UMOUNT(MNTPOINT1); + + if (!access(MNTPOINT2, F_OK) && tst_is_mounted(MNTPOINT2)) + SAFE_UMOUNT(MNTPOINT2); +} + +static void run(void) +{ + SAFE_MKDIR(MNTPOINT2, 0750); + SAFE_MOUNT(MNTPOINT1, MNTPOINT2, tst_device->fs_type, MS_BIND, NULL); + + TST_EXP_PASS(access(TESTFILE2, F_OK), "Accessing to '%s'", TESTFILE2); + TST_EXP_PASS(access(TESTDIR2, F_OK), "Accessing to '%s'", TESTDIR2); + + if (tst_is_mounted(MNTPOINT2)) + SAFE_UMOUNT(MNTPOINT2); + + SAFE_RMDIR(MNTPOINT2); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .needs_root = 1, + .format_device = 1, + .mntpoint = MNTPOINT1, + .all_filesystems = 1, + .skip_filesystems = (const char *const []){ + "exfat", + "vfat", + "ntfs", + NULL + }, }; - -int main(int argc, char *argv[]) -{ - int lc; - - tst_parse_opts(argc, argv, options, &help); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - TEST(mount(mntpoint_src, mntpoint_des, fstype, MS_BIND, NULL)); - - if (TEST_RETURN != 0) { - tst_resm(TFAIL | TTERRNO, "mount(2) failed"); - } else { - - if (open(file_des, O_CREAT | O_EXCL, S_IRWXU) == -1 && - errno == EEXIST) - tst_resm(TPASS, "bind mount is ok"); - else - tst_resm(TFAIL, "file %s is not available", - file_des); - - TEST(tst_umount(mntpoint_des)); - if (TEST_RETURN != 0) - tst_brkm(TBROK | TTERRNO, cleanup, - "umount(2) failed"); - } - } - - cleanup(); - tst_exit(); -} - -void setup(void) -{ - tst_require_root(); - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - tst_tmpdir(); - - SAFE_MKDIR(cleanup, mntpoint_src, DIR_MODE); - SAFE_MKDIR(cleanup, mntpoint_des, DIR_MODE); - - if (dflag) { - tst_mkfs(NULL, device, fstype, NULL, NULL); - - SAFE_MOUNT(cleanup, device, mntpoint_src, fstype, 0, NULL); - } - - SAFE_FILE_PRINTF(cleanup, file_src, "TEST FILE"); - - TEST_PAUSE; -} - -void cleanup(void) -{ - if (dflag) - if (tst_umount(mntpoint_src) != 0) - tst_brkm(TBROK | TTERRNO, NULL, "umount(2) failed"); - - tst_rmdir(); -} - -void help(void) -{ - printf("-T type : specifies the type of filesystem to be mounted. " - "Default ext2.\n"); - printf("-D device : device used for mounting.\n"); -} diff --git a/testcases/kernel/syscalls/mount/mount06.c b/testcases/kernel/syscalls/mount/mount06.c index 857f5f90..1e06cd58 100755 --- a/testcases/kernel/syscalls/mount/mount06.c +++ b/testcases/kernel/syscalls/mount/mount06.c @@ -1,34 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2013 Fujitsu Ltd. - * Author: DAN LI - * - * 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. - * + * Copyright (c) 2013 Fujitsu Ltd. Dan Li + * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ -/* - * DESCRIPTION - * Test for feature MS_MOVE of mount(2). - * "Move an existing mount point to the new location." +/*\ + * Test for feature MS_MOVE of mount, which moves an existing mount point to + * a new location. */ -#include +#include "tst_test.h" +#include #include -#include -#include - -#include "test.h" -#include "safe_macros.h" #ifndef MS_MOVE #define MS_MOVE 8192 @@ -38,126 +21,85 @@ #define MS_PRIVATE (1 << 18) #endif -#define MNTPOINT_SRC "mnt_src" -#define MNTPOINT_DES "mnt_des" -#define LINELENGTH 256 -#define DIR_MODE (S_IRWXU | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP) +#define MNTPOINT_SRC "mntpoint1" +#define MNTPOINT_DST "mntpoint2" -static int ismount(char *mntpoint); -static void setup(void); -static void cleanup(void); - -char *TCID = "mount06"; -int TST_TOTAL = 1; - -static const char *fs_type; -static const char *device; -static char path_name[PATH_MAX]; -static char mntpoint_src[PATH_MAX]; -static char mntpoint_des[PATH_MAX]; -static int mount_flag; - -int main(int argc, char *argv[]) -{ - int lc; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - SAFE_MOUNT(cleanup, device, mntpoint_src, fs_type, 0, NULL); - - TEST(mount(mntpoint_src, mntpoint_des, fs_type, MS_MOVE, NULL)); - - if (TEST_RETURN != 0) { - tst_resm(TFAIL | TTERRNO, "mount(2) failed"); - } else { - - if (!ismount(mntpoint_src) && ismount(mntpoint_des)) - tst_resm(TPASS, "move mount is ok"); - else - tst_resm(TFAIL, "move mount does not work"); - - TEST(tst_umount(mntpoint_des)); - if (TEST_RETURN != 0) - tst_brkm(TBROK | TTERRNO, cleanup, - "umount(2) failed"); - } - } - - cleanup(); - tst_exit(); -} - -int ismount(char *mntpoint) -{ - int ret = 0; - FILE *file; - char line[LINELENGTH]; - - file = fopen("/proc/mounts", "r"); - if (file == NULL) - tst_brkm(TFAIL | TERRNO, NULL, "Open /proc/mounts failed"); - - while (fgets(line, LINELENGTH, file) != NULL) { - if (strstr(line, mntpoint) != NULL) { - ret = 1; - break; - } - } - fclose(file); - return ret; -} +static char *tmppath; +static char *mntpoint_src; +static char *mntpoint_dst; +static char *tstfiles_src; +static char *tstfiles_dst; static void setup(void) { - tst_require_root(); - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - tst_tmpdir(); - - fs_type = tst_dev_fs_type(); - device = tst_acquire_device(cleanup); - - if (!device) - tst_brkm(TCONF, cleanup, "Failed to obtain block device"); - - tst_mkfs(cleanup, device, fs_type, NULL, NULL); - - if (getcwd(path_name, sizeof(path_name)) == NULL) - tst_brkm(TBROK, cleanup, "getcwd failed"); + tmppath = tst_tmpdir_path(); /* * Turn current dir into a private mount point being a parent * mount which is required by move mount. */ - SAFE_MOUNT(cleanup, path_name, path_name, "none", MS_BIND, NULL); + SAFE_MOUNT(tmppath, tmppath, "none", MS_BIND, NULL); + SAFE_MOUNT("none", tmppath, "none", MS_PRIVATE, NULL); - mount_flag = 1; + mntpoint_src = tst_tmpdir_genpath(MNTPOINT_SRC); + mntpoint_dst = tst_tmpdir_genpath(MNTPOINT_DST); + tstfiles_src = tst_tmpdir_genpath("%s/testfile", MNTPOINT_SRC); + tstfiles_dst = tst_tmpdir_genpath("%s/testfile", MNTPOINT_DST); - SAFE_MOUNT(cleanup, "none", path_name, "none", MS_PRIVATE, NULL); - - snprintf(mntpoint_src, PATH_MAX, "%s/%s", path_name, MNTPOINT_SRC); - snprintf(mntpoint_des, PATH_MAX, "%s/%s", path_name, MNTPOINT_DES); - - SAFE_MKDIR(cleanup, mntpoint_src, DIR_MODE); - SAFE_MKDIR(cleanup, mntpoint_des, DIR_MODE); - - TEST_PAUSE; + SAFE_MKDIR(mntpoint_dst, 0750); } static void cleanup(void) { - if (mount_flag && tst_umount(path_name) != 0) - tst_resm(TWARN | TERRNO, "umount(2) %s failed", path_name); + if (tst_is_mounted(mntpoint_src)) + SAFE_UMOUNT(mntpoint_src); - if (device) - tst_release_device(device); + if (tst_is_mounted(mntpoint_dst)) + SAFE_UMOUNT(mntpoint_dst); - tst_rmdir(); + if (tst_is_mounted(tmppath)) + SAFE_UMOUNT(tmppath); + + SAFE_RMDIR(mntpoint_dst); } + +static void run(void) +{ + SAFE_MOUNT(tst_device->dev, mntpoint_src, tst_device->fs_type, 0, NULL); + SAFE_FILE_PRINTF(tstfiles_src, "LTP TEST FILE"); + + SAFE_MOUNT(mntpoint_src, mntpoint_dst, tst_device->fs_type, MS_MOVE, NULL); + + TST_EXP_FAIL( + access(tstfiles_src, F_OK), + ENOENT, + "File %s doesn't exist", + tstfiles_src); + + TST_EXP_PASS( + access(tstfiles_dst, F_OK), + "File %s exists :", + tstfiles_dst); + + if (tst_is_mounted(mntpoint_src)) + SAFE_UMOUNT(mntpoint_src); + + if (tst_is_mounted(mntpoint_dst)) + SAFE_UMOUNT(mntpoint_dst); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .needs_root = 1, + .format_device = 1, + .mntpoint = MNTPOINT_SRC, + .all_filesystems = 1, + .skip_filesystems = (const char *const []){ + "exfat", + "vfat", + "ntfs", + NULL + }, +}; diff --git a/testcases/kernel/syscalls/mount/mount07.c b/testcases/kernel/syscalls/mount/mount07.c index eb3fb55a..30fd3080 100644 --- a/testcases/kernel/syscalls/mount/mount07.c +++ b/testcases/kernel/syscalls/mount/mount07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for MS_NOSYMFOLLOW mount option and is copied * from kernel selftests nosymfollow-test.c. * @@ -31,9 +29,9 @@ #define MNTPOINT "mntpoint" -static char test_file[PATH_MAX]; -static char link_file[PATH_MAX]; -static char temp_link_file[PATH_MAX]; +static char *test_file; +static char *link_file; +static char *temp_link_file; static int flag; static void setup_symlink(void) @@ -114,15 +112,9 @@ static void test_statfs(bool nosymfollow) static void setup(void) { - char *tmpdir = tst_get_tmpdir(); - - snprintf(test_file, PATH_MAX, "%s/%s/test_file", tst_get_tmpdir(), - MNTPOINT); - snprintf(link_file, PATH_MAX, "%s/%s/link_file", tst_get_tmpdir(), - MNTPOINT); - snprintf(temp_link_file, PATH_MAX, "%s/%s/temp_link_file", - tst_get_tmpdir(), MNTPOINT); - free(tmpdir); + test_file = tst_tmpdir_genpath("%s/test_file", MNTPOINT); + link_file = tst_tmpdir_genpath("%s/link_file", MNTPOINT); + temp_link_file = tst_tmpdir_genpath("%s/temp_link_file", MNTPOINT); } static void cleanup(void) diff --git a/testcases/kernel/syscalls/mount/mount08.c b/testcases/kernel/syscalls/mount/mount08.c new file mode 100644 index 00000000..fb2b2873 --- /dev/null +++ b/testcases/kernel/syscalls/mount/mount08.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Wei Gao + */ + +/*\ + * Verify that mount will raise ENOENT if we try to mount on magic links + * under /proc//fd/. + * If SELinux is enabled, the expected error also can be EACCES since + * SElinux plicy could be configured to block the operation. + */ + +#include "tst_test.h" +#include +#include "tst_safe_file_at.h" + +#define MNTPOINT "mntpoint" +#define FOO MNTPOINT "/foo" +#define BAR MNTPOINT "/bar" + +static int exp_errnos_num; +static int exp_errnos[] = { + ENOENT, + EACCES, +}; + +static void run(void) +{ + char path[PATH_MAX]; + int fd, proc_fd; + + fd = SAFE_OPEN(FOO, O_RDONLY | O_NONBLOCK, 0640); + + sprintf(path, "/proc/%d/fd/%d", getpid(), fd); + + proc_fd = SAFE_OPENAT(AT_FDCWD, path, O_PATH | O_NOFOLLOW); + + sprintf(path, "/proc/%d/fd/%d", getpid(), proc_fd); + + TST_EXP_FAIL_ARR( + mount(BAR, path, "", MS_BIND, 0), + exp_errnos, exp_errnos_num, + "mount(%s)", path + ); + + SAFE_CLOSE(fd); + SAFE_CLOSE(proc_fd); +} + +static void setup(void) +{ + exp_errnos_num = ARRAY_SIZE(exp_errnos) - 1; + + if (tst_selinux_enforcing()) + exp_errnos_num++; + + SAFE_TOUCH(FOO, 0777, NULL); + SAFE_TOUCH(BAR, 0777, NULL); +} + +static struct tst_test test = { + .setup = setup, + .test_all = run, + .needs_root = 1, + .mntpoint = MNTPOINT, + .tags = (const struct tst_tag[]) { + {"linux-git", "d80b065bb172"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/mount_setattr/.gitignore b/testcases/kernel/syscalls/mount_setattr/.gitignore index 52a77b9c..1654f27d 100644 --- a/testcases/kernel/syscalls/mount_setattr/.gitignore +++ b/testcases/kernel/syscalls/mount_setattr/.gitignore @@ -1 +1,2 @@ /mount_setattr01 +/mount_setattr02 diff --git a/testcases/kernel/syscalls/mount_setattr/mount_setattr01.c b/testcases/kernel/syscalls/mount_setattr/mount_setattr01.c index e500df28..eb32cd91 100644 --- a/testcases/kernel/syscalls/mount_setattr/mount_setattr01.c +++ b/testcases/kernel/syscalls/mount_setattr/mount_setattr01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic mount_setattr() test. * Test whether the basic mount attributes are set correctly. * diff --git a/testcases/kernel/syscalls/mount_setattr/mount_setattr02.c b/testcases/kernel/syscalls/mount_setattr/mount_setattr02.c new file mode 100644 index 00000000..bb246ed1 --- /dev/null +++ b/testcases/kernel/syscalls/mount_setattr/mount_setattr02.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Wei Gao + */ + +/*\ + * This test is checking if the propagation field of the + * mount_attr structure is handled properly. + * + * - EINVAL with propagation set to -1 + * - When propagation is set to 0 it's not changed + * - MS_SHARED turns propagation on + * - MS_SLAVE turns propagation off + * - MS_PRIVATE turns propagation off + */ + +#define _GNU_SOURCE + +#include +#include "tst_test.h" +#include "lapi/fsmount.h" +#include "tst_safe_stdio.h" + +static char *tmpdir; +static char slavedir[PATH_MAX]; +static int mounted; + +enum mount_type { + MOUNT_TYPE_SHARED, + MOUNT_TYPE_MASTER +}; + +static bool check_mount_type(const char *path, enum mount_type type_to_check) +{ + FILE *file = SAFE_FOPEN("/proc/self/mountinfo", "r"); + + char line[PATH_MAX]; + bool found = false; + + while (fgets(line, sizeof(line), file)) { + char mntpoint[PATH_MAX]; + char opts[256]; + + if (sscanf(line, "%*d %*d %*d:%*d %*s %255s %*s %255s", + mntpoint, opts) != 2) + continue; + + if (strcmp(mntpoint, path) != 0) + continue; + + switch (type_to_check) { + case MOUNT_TYPE_SHARED: + if (strstr(opts, "shared:") != NULL) + found = true; + break; + case MOUNT_TYPE_MASTER: + if (strstr(opts, "master:") != NULL) + found = true; + break; + default: + tst_res(TFAIL, "Unexpected mount_type value: %d", type_to_check); + } + } + + fclose(file); + return found; +} + +static void cleanup(void) +{ + if (mounted) + SAFE_UMOUNT(tmpdir); +} + +static void setup(void) +{ + tmpdir = tst_tmpdir_path(); + sprintf(slavedir, "%s/slavedir", tmpdir); + SAFE_UNSHARE(CLONE_NEWNS); + SAFE_MOUNT(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0); + SAFE_MOUNT("ltp-mount_setattr", tmpdir, "tmpfs", MS_NOATIME | MS_NODEV, ""); + SAFE_MKDIR(slavedir, 0777); + mounted = 1; +} + +static void run(void) +{ + struct mount_attr attr = { + .attr_set = 0, + .attr_clr = 0, + }; + + TST_EXP_PASS_SILENT(mount_setattr(-1, tmpdir, 0, &attr, sizeof(attr))); + TST_EXP_EQ_LI(check_mount_type(tmpdir, MOUNT_TYPE_SHARED), 0); + + attr.propagation = -1; + TST_EXP_FAIL_SILENT(mount_setattr(-1, tmpdir, 0, &attr, sizeof(attr)), EINVAL); + TST_EXP_EQ_LI(check_mount_type(tmpdir, MOUNT_TYPE_SHARED), 0); + + attr.propagation = MS_SHARED; + TST_EXP_PASS_SILENT(mount_setattr(-1, tmpdir, 0, &attr, sizeof(attr))); + TST_EXP_EQ_LI(check_mount_type(tmpdir, MOUNT_TYPE_SHARED), 1); + + attr.propagation = 0; + TST_EXP_PASS_SILENT(mount_setattr(-1, tmpdir, 0, &attr, sizeof(attr))); + TST_EXP_EQ_LI(check_mount_type(tmpdir, MOUNT_TYPE_SHARED), 1); + + attr.propagation = MS_SLAVE; + SAFE_MOUNT(tmpdir, slavedir, "none", MS_BIND, NULL); + TST_EXP_EQ_LI(check_mount_type(slavedir, MOUNT_TYPE_SHARED), 1); + TST_EXP_PASS_SILENT(mount_setattr(-1, slavedir, 0, &attr, sizeof(attr))); + TST_EXP_EQ_LI(check_mount_type(slavedir, MOUNT_TYPE_MASTER), 1); + TST_EXP_EQ_LI(check_mount_type(slavedir, MOUNT_TYPE_SHARED), 0); + SAFE_UMOUNT(slavedir); + + attr.propagation = MS_PRIVATE; + TST_EXP_PASS_SILENT(mount_setattr(-1, tmpdir, 0, &attr, sizeof(attr))); + TST_EXP_EQ_LI(check_mount_type(tmpdir, MOUNT_TYPE_SHARED), 0); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/move_mount/.gitignore b/testcases/kernel/syscalls/move_mount/.gitignore index 83ae4014..ddfe1012 100755 --- a/testcases/kernel/syscalls/move_mount/.gitignore +++ b/testcases/kernel/syscalls/move_mount/.gitignore @@ -1,2 +1,3 @@ /move_mount01 /move_mount02 +/move_mount03 diff --git a/testcases/kernel/syscalls/move_mount/move_mount03.c b/testcases/kernel/syscalls/move_mount/move_mount03.c new file mode 100644 index 00000000..a9429af7 --- /dev/null +++ b/testcases/kernel/syscalls/move_mount/move_mount03.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Christian Brauner + * Copyright (c) 2023 Wei Gao + */ + +/*\ + * Test allow to mount beneath top mount feature added in kernel 6.5: + * 6ac392815628 ("fs: allow to mount beneath top mount") + * + * Test based on: + * https://github.com/brauner/move-mount-beneath + * + * See also: + * + * - https://lore.kernel.org/all/20230202-fs-move-mount-replace-v4-0-98f3d80d7eaa@kernel.org/ + * - https://lwn.net/Articles/930591/ + * - https://github.com/brauner/move-mount-beneath + */ + +/* + * Test create for following commit: + * commit 6ac392815628f317fcfdca1a39df00b9cc4ebc8b + * Author: Christian Brauner + * Date: Wed May 3 13:18:42 2023 +0200 + * fs: allow to mount beneath top mount + * + * Above commit has heavily commented but i found following commit + * contain simple summary of this feature for easy understanding: + * + * commit c0a572d9d32fe1e95672f24e860776dba0750a38 + * Author: Linus Torvalds + * TL;DR: + * + * > mount -t ext4 /dev/sda /mnt + * | + * --/mnt /dev/sda ext4 + * + * > mount --beneath -t xfs /dev/sdb /mnt + * | + * --/mnt /dev/sdb xfs + * --/mnt /dev/sda ext4 + * + * > umount /mnt + * | + * --/mnt /dev/sdb xfs + * + * So base above scenario design following scenario for LTP check: + * + * > mount -t tmpfs /DIRA + * | + * --/DIRA(create A file within DIRA) + * + * > mount -t tmpfs /DIRB + * | + * --/DIRA(create B file within DIRB) + * + * > move_mount --beneath /DIRA /DIRB + * | + * --/mnt /DIRA /DIRB + * --/mnt /DIRB + * + * If you check content of /DIRB, you can see file B + * + * > umount /DIRB + * | + * --/mnt /DIRA /DIRB + * Check content of /DIRB, you can see file A exist since + * current /DIRB mount source is already become /DIRA + */ + +#include + +#include "tst_test.h" +#include "lapi/fsmount.h" +#include "lapi/sched.h" + +#define DIRA "LTP_DIR_A" +#define DIRB "LTP_DIR_B" + +static int fda = -1, fdb = -1; + +static void run(void) +{ + SAFE_MOUNT("none", DIRA, "tmpfs", 0, 0); + SAFE_MOUNT("none", DIRB, "tmpfs", 0, 0); + SAFE_TOUCH(DIRA "/A", 0, NULL); + SAFE_TOUCH(DIRB "/B", 0, NULL); + + fda = open_tree(AT_FDCWD, DIRA, OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE); + if (fda == -1) + tst_brk(TBROK | TERRNO, "open_tree() failed"); + + fdb = SAFE_OPEN(DIRB, O_PATH | O_NOFOLLOW, 0666); + TST_EXP_PASS(move_mount(fda, "", fdb, "", + MOVE_MOUNT_BENEATH | MOVE_MOUNT_F_EMPTY_PATH | + MOVE_MOUNT_T_EMPTY_PATH)); + SAFE_CLOSE(fda); + SAFE_CLOSE(fdb); + + TST_EXP_PASS(access(DIRB "/B", F_OK)); + TST_EXP_PASS(access(DIRA "/A", F_OK)); + TST_EXP_FAIL(access(DIRB "/A", F_OK), ENOENT, "A should not exist"); + + SAFE_UMOUNT(DIRB); + TST_EXP_PASS(access(DIRB "/A", F_OK)); + + SAFE_UMOUNT(DIRB); + SAFE_UMOUNT(DIRA); +} + +static void setup(void) +{ + SAFE_MKDIR(DIRA, 0777); + SAFE_MKDIR(DIRB, 0777); +} + +static void cleanup(void) +{ + if (fda >= 0) + SAFE_CLOSE(fda); + + if (fdb >= 0) + SAFE_CLOSE(fdb); + + if (tst_is_mounted_at_tmpdir(DIRA)) + SAFE_UMOUNT(DIRA); + + if (tst_is_mounted_at_tmpdir(DIRB)) + SAFE_UMOUNT(DIRB); + + if (tst_is_mounted_at_tmpdir(DIRB)) + SAFE_UMOUNT(DIRB); +} + +static struct tst_test test = { + .test_all = run, + .needs_root = 1, + .min_kver = "6.5", + .needs_tmpdir = 1, + .setup = setup, + .cleanup = cleanup, + .tags = (const struct tst_tag[]) { + {"linux-git", "6ac392815628f"}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/move_pages/move_pages04.c b/testcases/kernel/syscalls/move_pages/move_pages04.c index f53453ab..e2a1fc4f 100755 --- a/testcases/kernel/syscalls/move_pages/move_pages04.c +++ b/testcases/kernel/syscalls/move_pages/move_pages04.c @@ -1,58 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2008 Vijay Kumar B. - * - * Based on testcases/kernel/syscalls/waitpid/waitpid01.c - * Original copyright message: - * * Copyright (c) International Business Machines Corp., 2001 - * - * 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 */ -/* - * NAME - * move_pages04.c +/*\ + * Verify that move_pages() properly reports failures when the memory area is + * not valid, no page is mapped yet or the shared zero page is mapped. * - * DESCRIPTION - * Failure when page does not exit. + * [Algorithm] * - * ALGORITHM + * #. Pass the address of a valid memory area where no page is + * mapped yet (not read/written), the address of a valid memory area + * where the shared zero page is mapped (read, but not written to) + * and the address of an invalid memory area as page addresses to + * move_pages(). + * #. Check if the corresponding status for "no page mapped" is set to + * -ENOENT. Note that kernels >= 4.3 [1] and < 6.12 [2] wrongly returned + * -EFAULT by accident. + * #. Check if the corresponding status for "shared zero page" is set to: + * -EFAULT. Note that kernels < 4.3 [1] wrongly returned -ENOENT. + * #. Check if the corresponding status for "invalid memory area" is set + * to -EFAULT. * - * 1. Pass zero page (allocated, but not written to) as one of the - * page addresses to move_pages(). - * 2. Check if the corresponding status is set to: - * -ENOENT for kernels < 4.3 - * -EFAULT for kernels >= 4.3 [1] - * - * [1] - * d899844e9c98 "mm: fix status code which move_pages() returns for zero page" - * - * USAGE: - * move_pages04 [-c n] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 05/2008 Vijay Kumar - * Initial Version. - * - * Restrictions - * None + * - [1] d899844e9c98 "mm: fix status code which move_pages() returns for zero page" + * - [2] 7dff875c9436 "mm/migrate: convert add_page_for_migration() from follow_page() to folio_walk" */ #include @@ -61,124 +33,134 @@ #include #include #include -#include "test.h" +#include "tst_test.h" #include "move_pages_support.h" -#define TEST_PAGES 2 +#define TEST_PAGES 4 #define TEST_NODES 2 #define TOUCHED_PAGES 1 -#define UNTOUCHED_PAGE (TEST_PAGES - 1) +#define NO_PAGE TOUCHED_PAGES +#define ZERO_PAGE (NO_PAGE + 1) +#define INVALID_PAGE (ZERO_PAGE + 1) -void setup(void); -void cleanup(void); - -char *TCID = "move_pages04"; -int TST_TOTAL = 1; - -typedef void (*sighandler_t) (int); - -int main(int argc, char **argv) +static void run(void) { - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - #ifdef HAVE_NUMA_V2 unsigned int i; - int lc; unsigned int from_node; unsigned int to_node; - int ret, exp_status; - - if ((tst_kvercmp(4, 3, 0)) >= 0) - exp_status = -EFAULT; - else - exp_status = -ENOENT; + int ret; + void *pages[TEST_PAGES] = { 0 }; + int nodes[TEST_PAGES]; + int status[TEST_PAGES]; + unsigned long onepage = get_page_size(); + char tmp; ret = get_allowed_nodes(NH_MEMS, 2, &from_node, &to_node); if (ret < 0) - tst_brkm(TBROK | TERRNO, cleanup, "get_allowed_nodes: %d", ret); + tst_brk(TBROK | TERRNO, "get_allowed_nodes: %d", ret); - /* check for looping state if -i option is given */ - for (lc = 0; TEST_LOOPING(lc); lc++) { - void *pages[TEST_PAGES] = { 0 }; - int nodes[TEST_PAGES]; - int status[TEST_PAGES]; - unsigned long onepage = get_page_size(); + ret = alloc_pages_on_node(pages, TOUCHED_PAGES, from_node); + if (ret == -1) + tst_brk(TBROK, "failed allocating memory on node %d", + from_node); - /* reset tst_count in case we are looping */ - tst_count = 0; + /* + * Allocate memory and do not touch it. Consequently, no + * page will be faulted in / mapped into the page tables. + */ + pages[NO_PAGE] = numa_alloc_onnode(onepage, from_node); + if (pages[NO_PAGE] == NULL) + tst_brk(TBROK, "failed allocating memory on node %d", + from_node); - ret = alloc_pages_on_node(pages, TOUCHED_PAGES, from_node); - if (ret == -1) - continue; + /* + * Allocate memory, read from it, but do not write to it. This + * will populate the shared zeropage. + */ + pages[ZERO_PAGE] = numa_alloc_onnode(onepage, from_node); + if (pages[ZERO_PAGE] == NULL) + tst_brk(TBROK, "failed allocating memory on node %d", + from_node); + /* Make the compiler not optimize-out the read. */ + tmp = *((char *)pages[ZERO_PAGE]); + asm volatile("" : "+r" (tmp)); - /* Allocate page and do not touch it. */ - pages[UNTOUCHED_PAGE] = numa_alloc_onnode(onepage, from_node); - if (pages[UNTOUCHED_PAGE] == NULL) { - tst_resm(TBROK, "failed allocating page on node %d", - from_node); - goto err_free_pages; - } + /* + * Temporarily allocate memory and free it immediately. Do this + * as the last step so the area won't get reused before we're + * done. + */ + pages[INVALID_PAGE] = numa_alloc_onnode(onepage, from_node); + if (pages[INVALID_PAGE] == NULL) + tst_brk(TBROK, "failed allocating memory on node %d", + from_node); + numa_free(pages[INVALID_PAGE], onepage); - for (i = 0; i < TEST_PAGES; i++) - nodes[i] = to_node; + for (i = 0; i < TEST_PAGES; i++) + nodes[i] = to_node; - ret = numa_move_pages(0, TEST_PAGES, pages, nodes, - status, MPOL_MF_MOVE); - if (ret == -1) { - tst_resm(TFAIL | TERRNO, - "move_pages unexpectedly failed"); - goto err_free_pages; - } else if (ret > 0) { - tst_resm(TINFO, "move_pages() returned %d", ret); - } + ret = numa_move_pages(0, TEST_PAGES, pages, nodes, + status, MPOL_MF_MOVE); + if (ret == -1) { + tst_res(TFAIL | TERRNO, + "move_pages unexpectedly failed"); + goto err_free_pages; + } else if (ret > 0) { + tst_res(TINFO, "move_pages() returned %d", ret); + } - if (status[UNTOUCHED_PAGE] == exp_status) { - tst_resm(TPASS, "status[%d] has expected value", - UNTOUCHED_PAGE); - } else { - tst_resm(TFAIL, "status[%d] is %s, expected %s", - UNTOUCHED_PAGE, - tst_strerrno(-status[UNTOUCHED_PAGE]), - tst_strerrno(-exp_status)); - } + if (status[NO_PAGE] == -ENOENT) { + tst_res(TPASS, "status[%d] has expected value", + NO_PAGE); + } else { + tst_res(TFAIL, "status[%d] is %s, expected %s", + NO_PAGE, + tst_strerrno(-status[NO_PAGE]), + tst_strerrno(ENOENT)); + } + + if (status[ZERO_PAGE] == -EFAULT) { + tst_res(TPASS, "status[%d] has expected value", + ZERO_PAGE); + } else { + tst_res(TFAIL, "status[%d] is %s, expected %s", + ZERO_PAGE, + tst_strerrno(-status[ZERO_PAGE]), + tst_strerrno(EFAULT)); + } + + if (status[INVALID_PAGE] == -EFAULT) { + tst_res(TPASS, "status[%d] has expected value", + INVALID_PAGE); + } else { + tst_res(TFAIL, "status[%d] is %s, expected %s", + INVALID_PAGE, + tst_strerrno(-status[INVALID_PAGE]), + tst_strerrno(EFAULT)); + } err_free_pages: - /* This is capable of freeing both the touched and - * untouched pages. - */ - free_pages(pages, TEST_PAGES); - } + /* Memory for the invalid page was already freed. */ + pages[INVALID_PAGE] = NULL; + /* This is capable of freeing all memory we allocated. */ + free_pages(pages, TEST_PAGES); #else - tst_resm(TCONF, NUMA_ERROR_MSG); + tst_res(TCONF, NUMA_ERROR_MSG); #endif - - cleanup(); - tst_exit(); } -/* - * setup() - performs all ONE TIME setup for this test - */ void setup(void) { - - tst_sig(FORK, DEF_HANDLER, cleanup); - check_config(TEST_NODES); - - /* Pause if that option was specified - * TEST_PAUSE contains the code to fork the test with the -c option. - */ - TEST_PAUSE; } -/* - * cleanup() - performs all ONE TIME cleanup for this test at completion - */ -void cleanup(void) -{ - -} +static struct tst_test test = { + .test_all = run, + .setup = setup, + .tags = (const struct tst_tag[]) { + {"linux-git", "d899844e9c98"}, + {"linux-git", "7dff875c9436"}, + {} + } +}; diff --git a/testcases/kernel/syscalls/move_pages/move_pages12.c b/testcases/kernel/syscalls/move_pages/move_pages12.c index fd7017d7..440773a3 100755 --- a/testcases/kernel/syscalls/move_pages/move_pages12.c +++ b/testcases/kernel/syscalls/move_pages/move_pages12.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * *Test 1* * * This is a regression test for the race condition between move_pages() @@ -76,7 +74,7 @@ #ifdef HAVE_NUMA_V2 -#define LOOPS 1000 +#define LOOPS 10000 #define PATH_MEMINFO "/proc/meminfo" #define PATH_NR_HUGEPAGES "/proc/sys/vm/nr_hugepages" #define PATH_HUGEPAGES "/sys/kernel/mm/hugepages/" @@ -102,7 +100,7 @@ static void *addr; static int do_soft_offline(int tpgs) { if (madvise(addr, tpgs * hpsz, MADV_SOFT_OFFLINE) == -1) { - if (errno != EINVAL && errno != EBUSY) + if (errno != EINVAL && errno != EBUSY && errno != ENOMEM) tst_res(TFAIL | TERRNO, "madvise failed"); return errno; } @@ -154,6 +152,8 @@ static void do_test(unsigned int n) pid_t cpid = -1; int status; + SAFE_FILE_PRINTF("/proc/sys/vm/compact_memory", "1"); + addr = SAFE_MMAP(NULL, tcases[n].tpages * hpsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); @@ -269,6 +269,7 @@ static void setup(void) pgsz = (int)get_page_size(); SAFE_FILE_LINES_SCANF(PATH_MEMINFO, "Hugepagesize: %d", &hpsz); + SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "3"); SAFE_FILE_LINES_SCANF(PATH_MEMINFO, "MemFree: %ld", &memfree); tst_res(TINFO, "Free RAM %ld kB", memfree); @@ -339,7 +340,7 @@ static struct tst_test test = { .cleanup = cleanup, .test = do_test, .tcnt = ARRAY_SIZE(tcases), - .max_runtime = 240, + .runtime = 240, .tags = (const struct tst_tag[]) { {"linux-git", "e66f17ff7177"}, {"linux-git", "c9d398fa2378"}, diff --git a/testcases/kernel/syscalls/mprotect/mprotect02.c b/testcases/kernel/syscalls/mprotect/mprotect02.c index af282bba..de894868 100755 --- a/testcases/kernel/syscalls/mprotect/mprotect02.c +++ b/testcases/kernel/syscalls/mprotect/mprotect02.c @@ -89,7 +89,7 @@ int main(int ac, char **av) addr = SAFE_MMAP(cleanup, 0, sizeof(buf), PROT_READ, MAP_SHARED, fd, 0); - if ((pid = FORK_OR_VFORK()) == -1) + if ((pid = tst_fork()) == -1) tst_brkm(TBROK | TERRNO, cleanup, "fork #1 failed"); if (pid == 0) { @@ -118,7 +118,7 @@ int main(int ac, char **av) TEST(mprotect(addr, sizeof(buf), PROT_WRITE)); if (TEST_RETURN != -1) { - if ((pid = FORK_OR_VFORK()) == -1) + if ((pid = tst_fork()) == -1) tst_brkm(TBROK | TERRNO, cleanup, "fork #2 failed"); diff --git a/testcases/kernel/syscalls/mprotect/mprotect03.c b/testcases/kernel/syscalls/mprotect/mprotect03.c index ed0c1a7d..8ef64f21 100755 --- a/testcases/kernel/syscalls/mprotect/mprotect03.c +++ b/testcases/kernel/syscalls/mprotect/mprotect03.c @@ -95,7 +95,7 @@ int main(int ac, char **av) TEST(mprotect(addr, strlen(buf), PROT_READ)); if (TEST_RETURN != -1) { - if ((pid = FORK_OR_VFORK()) == -1) { + if ((pid = tst_fork()) == -1) { tst_brkm(TBROK, cleanup, "fork failed"); } diff --git a/testcases/kernel/syscalls/mprotect/mprotect05.c b/testcases/kernel/syscalls/mprotect/mprotect05.c index 2b15f5be..cd6d81a0 100644 --- a/testcases/kernel/syscalls/mprotect/mprotect05.c +++ b/testcases/kernel/syscalls/mprotect/mprotect05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Testcase to check the mprotect(2) system call split and merge. * * https://bugzilla.kernel.org/show_bug.cgi?id=217061 diff --git a/testcases/kernel/syscalls/mq_notify/mq_notify02.c b/testcases/kernel/syscalls/mq_notify/mq_notify02.c index d979a4e9..51924943 100755 --- a/testcases/kernel/syscalls/mq_notify/mq_notify02.c +++ b/testcases/kernel/syscalls/mq_notify/mq_notify02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test verifies that mq_notify() fails with EINVAL when invalid input * arguments are given. */ diff --git a/testcases/kernel/syscalls/mq_notify/mq_notify03.c b/testcases/kernel/syscalls/mq_notify/mq_notify03.c index bf6898cd..30e94ab1 100644 --- a/testcases/kernel/syscalls/mq_notify/mq_notify03.c +++ b/testcases/kernel/syscalls/mq_notify/mq_notify03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test for NULL pointer dereference in mq_notify(CVE-2021-38604) * * References links: diff --git a/testcases/kernel/syscalls/mq_timedreceive/mq_timedreceive01.c b/testcases/kernel/syscalls/mq_timedreceive/mq_timedreceive01.c index be437e19..20f25477 100755 --- a/testcases/kernel/syscalls/mq_timedreceive/mq_timedreceive01.c +++ b/testcases/kernel/syscalls/mq_timedreceive/mq_timedreceive01.c @@ -22,56 +22,43 @@ static void *bad_addr; static struct test_case tcase[] = { { .fd = &fd, - .len = 0, .send = 1, - .ret = 0, - .err = 0, }, { .fd = &fd, .len = 1, .send = 1, - .ret = 0, - .err = 0, }, { .fd = &fd, .len = MAX_MSGSIZE, .send = 1, - .ret = 0, - .err = 0, }, { .fd = &fd, .len = 1, .send = 1, .prio = MQ_PRIO_MAX - 1, - .ret = 0, - .err = 0, }, { .fd = &fd, .invalid_msg = 1, - .len = 0, .send = 1, .ret = -1, .err = EMSGSIZE, }, { .fd = &fd_invalid, - .len = 0, .ret = -1, .err = EBADF, }, { .fd = &fd_maxint, - .len = 0, .ret = -1, .err = EBADF, }, { .fd = &fd_root, - .len = 0, .ret = -1, .err = EBADF, }, @@ -85,7 +72,6 @@ static struct test_case tcase[] = { .fd = &fd, .len = 16, .tv_sec = -1, - .tv_nsec = 0, .rq = &ts, .ret = -1, .err = EINVAL, @@ -93,7 +79,6 @@ static struct test_case tcase[] = { { .fd = &fd, .len = 16, - .tv_sec = 0, .tv_nsec = -1, .rq = &ts, .ret = -1, @@ -102,7 +87,6 @@ static struct test_case tcase[] = { { .fd = &fd, .len = 16, - .tv_sec = 0, .tv_nsec = 1000000000, .rq = &ts, .ret = -1, @@ -145,32 +129,15 @@ static void setup(void) setup_common(); } -static void do_test(unsigned int i) +static void verify_mqt_receive(unsigned int i, pid_t pid) { struct time64_variants *tv = &variants[tst_variant]; const struct test_case *tc = &tcase[i]; + size_t len = MAX_MSGSIZE; + char rmsg[MAX_MSGSIZE]; + void *abs_timeout; unsigned int j; unsigned int prio; - size_t len = MAX_MSGSIZE; - char rmsg[len]; - pid_t pid = -1; - void *abs_timeout; - - tst_ts_set_sec(&ts, tc->tv_sec); - tst_ts_set_nsec(&ts, tc->tv_nsec); - - if (tc->signal) - pid = set_sig(tc->rq, tv->clock_gettime); - - if (tc->timeout) - set_timeout(tc->rq, tv->clock_gettime); - - if (tc->send) { - if (tv->mqt_send(*tc->fd, smsg, tc->len, tc->prio, NULL) < 0) { - tst_res(TFAIL | TTERRNO, "mq_timedsend() failed"); - return; - } - } if (tc->invalid_msg) len -= 1; @@ -199,7 +166,7 @@ static void do_test(unsigned int i) return; } - if (tc->len != TST_RET) { + if ((long)tc->len != TST_RET) { tst_res(TFAIL, "mq_timedreceive() wrong length %ld, expected %u", TST_RET, tc->len); return; @@ -224,6 +191,60 @@ static void do_test(unsigned int i) TST_RET, prio, len); } +static void test_bad_addr(unsigned int i) +{ + struct time64_variants *tv = &variants[tst_variant]; + pid_t pid; + int status; + + pid = SAFE_FORK(); + if (!pid) { + verify_mqt_receive(i, pid); + exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFEXITED(status) && !WEXITSTATUS(status)) + return; + + if (tv->ts_type == TST_LIBC_TIMESPEC && + WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "Child killed by expected signal"); + return; + } + + tst_res(TFAIL, "Child %s", tst_strstatus(status)); +} + +static void do_test(unsigned int i) +{ + struct time64_variants *tv = &variants[tst_variant]; + const struct test_case *tc = &tcase[i]; + pid_t pid = -1; + + tst_ts_set_sec(&ts, tc->tv_sec); + tst_ts_set_nsec(&ts, tc->tv_nsec); + + if (tc->bad_ts_addr) { + test_bad_addr(i); + return; + } + + if (tc->signal) + pid = set_sig(tc->rq, tv->clock_gettime); + + if (tc->timeout) + set_timeout(tc->rq, tv->clock_gettime); + + if (tc->send && tv->mqt_send(*tc->fd, smsg, tc->len, tc->prio, NULL) < 0) { + tst_res(TFAIL | TTERRNO, "mq_timedsend() failed"); + return; + } + + verify_mqt_receive(i, pid); +} + static struct tst_test test = { .tcnt = ARRAY_SIZE(tcase), .test = do_test, diff --git a/testcases/kernel/syscalls/mq_timedsend/mq_timedsend01.c b/testcases/kernel/syscalls/mq_timedsend/mq_timedsend01.c index 33413140..244092c6 100755 --- a/testcases/kernel/syscalls/mq_timedsend/mq_timedsend01.c +++ b/testcases/kernel/syscalls/mq_timedsend/mq_timedsend01.c @@ -22,28 +22,19 @@ static void *bad_addr; static struct test_case tcase[] = { { .fd = &fd, - .len = 0, - .ret = 0, - .err = 0, }, { .fd = &fd, .len = 1, - .ret = 0, - .err = 0, }, { .fd = &fd, .len = MAX_MSGSIZE, - .ret = 0, - .err = 0, }, { .fd = &fd, .len = 1, .prio = MQ_PRIO_MAX - 1, - .ret = 0, - .err = 0, }, { .fd = &fd, @@ -53,19 +44,16 @@ static struct test_case tcase[] = { }, { .fd = &fd_invalid, - .len = 0, .ret = -1, .err = EBADF, }, { .fd = &fd_maxint, - .len = 0, .ret = -1, .err = EBADF, }, { .fd = &fd_root, - .len = 0, .ret = -1, .err = EBADF, }, @@ -86,7 +74,6 @@ static struct test_case tcase[] = { .fd = &fd, .len = 16, .tv_sec = -1, - .tv_nsec = 0, .rq = &ts, .send = 1, .ret = -1, @@ -95,7 +82,6 @@ static struct test_case tcase[] = { { .fd = &fd, .len = 16, - .tv_sec = 0, .tv_nsec = -1, .rq = &ts, .send = 1, @@ -105,7 +91,6 @@ static struct test_case tcase[] = { { .fd = &fd, .len = 16, - .tv_sec = 0, .tv_nsec = 1000000000, .rq = &ts, .send = 1, @@ -158,34 +143,15 @@ static void setup(void) setup_common(); } -static void do_test(unsigned int i) +static void verify_mqt_send_receive(unsigned int i, pid_t pid) { struct time64_variants *tv = &variants[tst_variant]; const struct test_case *tc = &tcase[i]; unsigned int j; unsigned int prio; - size_t len = MAX_MSGSIZE; - char rmsg[len]; - pid_t pid = -1; + char rmsg[MAX_MSGSIZE]; void *msg_ptr, *abs_timeout; - tst_ts_set_sec(&ts, tc->tv_sec); - tst_ts_set_nsec(&ts, tc->tv_nsec); - - if (tc->signal) - pid = set_sig(tc->rq, tv->clock_gettime); - - if (tc->timeout) - set_timeout(tc->rq, tv->clock_gettime); - - if (tc->send) { - for (j = 0; j < MSG_LENGTH; j++) - if (tv->mqt_send(*tc->fd, smsg, tc->len, tc->prio, NULL) < 0) { - tst_res(TFAIL | TTERRNO, "mq_timedsend() failed"); - return; - } - } - if (tc->bad_msg_addr) msg_ptr = bad_addr; else @@ -215,7 +181,7 @@ static void do_test(unsigned int i) return; } - TEST(tv->mqt_receive(*tc->fd, rmsg, len, &prio, tst_ts_get(tc->rq))); + TEST(tv->mqt_receive(*tc->fd, rmsg, MAX_MSGSIZE, &prio, tst_ts_get(tc->rq))); if (*tc->fd == fd) cleanup_queue(fd); @@ -235,7 +201,7 @@ static void do_test(unsigned int i) } } - if (tc->len != TST_RET) { + if ((long)tc->len != TST_RET) { tst_res(TFAIL, "mq_timedreceive() wrong length %ld, expected %u", TST_RET, tc->len); return; @@ -256,8 +222,66 @@ static void do_test(unsigned int i) } } - tst_res(TPASS, "mq_timedreceive() returned %ld, priority %u, length: %zu", - TST_RET, prio, len); + tst_res(TPASS, "mq_timedreceive() returned %ld, priority %u, length: %i", + TST_RET, prio, MAX_MSGSIZE); +} + +static void test_bad_addr(unsigned int i) +{ + struct time64_variants *tv = &variants[tst_variant]; + pid_t pid; + int status; + + pid = SAFE_FORK(); + if (!pid) { + verify_mqt_send_receive(i, pid); + _exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFEXITED(status) && !WEXITSTATUS(status)) + return; + + if (tv->ts_type == TST_LIBC_TIMESPEC && + WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "Child killed by expected signal"); + return; + } + + tst_res(TFAIL, "Child %s", tst_strstatus(status)); +} + +static void do_test(unsigned int i) +{ + struct time64_variants *tv = &variants[tst_variant]; + const struct test_case *tc = &tcase[i]; + unsigned int j; + pid_t pid = -1; + + tst_ts_set_sec(&ts, tc->tv_sec); + tst_ts_set_nsec(&ts, tc->tv_nsec); + + if (tc->bad_ts_addr || tc->bad_msg_addr) { + test_bad_addr(i); + return; + } + + if (tc->signal) + pid = set_sig(tc->rq, tv->clock_gettime); + + if (tc->timeout) + set_timeout(tc->rq, tv->clock_gettime); + + if (tc->send) { + for (j = 0; j < MSG_LENGTH; j++) + if (tv->mqt_send(*tc->fd, smsg, tc->len, tc->prio, NULL) < 0) { + tst_res(TFAIL | TTERRNO, "mq_timedsend() failed"); + return; + } + } + + verify_mqt_send_receive(i, pid); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/mremap/Makefile b/testcases/kernel/syscalls/mremap/Makefile index 190b7659..9f5aca9e 100755 --- a/testcases/kernel/syscalls/mremap/Makefile +++ b/testcases/kernel/syscalls/mremap/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpipc +LTPLIBS = ipc include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/mremap/mremap06.c b/testcases/kernel/syscalls/mremap/mremap06.c index a1926275..5820ad75 100644 --- a/testcases/kernel/syscalls/mremap/mremap06.c +++ b/testcases/kernel/syscalls/mremap/mremap06.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Bug reproducer for 7e7757876f25 ("mm/mremap: fix vm_pgoff in vma_merge() case 3") */ @@ -18,15 +16,16 @@ #include #include #include +#include #include "tst_test.h" #include "tst_safe_macros.h" -#define NUM_PAGES 3 +#define NUM_GRANULARITYS 3 static int fd; static char *buf, *buf2; -static int page_size, mmap_size, mremap_size; +static int mmap_size, mremap_size; static struct tcase { size_t incompatible; @@ -37,11 +36,11 @@ static struct tcase { }, { .incompatible = 3, - .desc = "third page's mapping incompatible", + .desc = "third MMAP_GRANULARITY's mapping incompatible", }, { .incompatible = 1, - .desc = "first page's mapping incompatible", + .desc = "first MMAP_GRANULARITY's mapping incompatible", }, }; @@ -51,7 +50,7 @@ static int check_pages(void) char val; for (i = 0; i < (int)ARRAY_SIZE(tcases); i++) { - val = buf[i * page_size]; + val = buf[i * MMAP_GRANULARITY]; if (val != 0x30 + i) { tst_res(TFAIL, "page %d wrong value %d (0x%x)", i, val - 0x30, val); fail = 1; @@ -70,19 +69,20 @@ static void do_test(unsigned int n) buf = SAFE_MMAP(0, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - buf2 = mremap(buf + page_size, page_size, page_size, + buf2 = mremap(buf + MMAP_GRANULARITY, MMAP_GRANULARITY, MMAP_GRANULARITY, MREMAP_MAYMOVE|MREMAP_FIXED, buf + mremap_size); if (buf2 == MAP_FAILED) tst_brk(TBROK, "mremap() failed"); if (tc->incompatible) { - ret = mprotect(buf + (tc->incompatible-1)*page_size, page_size, PROT_READ); + ret = mprotect(buf + (tc->incompatible-1)*MMAP_GRANULARITY, + MMAP_GRANULARITY, PROT_READ); if (ret == -1) tst_brk(TBROK, "mprotect() failed"); } - buf2 = mremap(buf + mremap_size, page_size, page_size, - MREMAP_MAYMOVE|MREMAP_FIXED, buf + page_size); + buf2 = mremap(buf + mremap_size, MMAP_GRANULARITY, MMAP_GRANULARITY, + MREMAP_MAYMOVE|MREMAP_FIXED, buf + MMAP_GRANULARITY); if (buf2 == MAP_FAILED) tst_brk(TBROK, "mremap() failed"); @@ -96,20 +96,24 @@ static void setup(void) { int ret, i; - page_size = getpagesize(); - mmap_size = (NUM_PAGES+1) * page_size; - mremap_size = NUM_PAGES * page_size; + mmap_size = (NUM_GRANULARITYS+1) * MMAP_GRANULARITY; + mremap_size = NUM_GRANULARITYS * MMAP_GRANULARITY; fd = SAFE_OPEN("testfile", O_CREAT | O_RDWR | O_TRUNC, 0600); ret = fallocate(fd, 0, 0, mmap_size); - if (ret == -1) + if (ret != 0) { + if (tst_fs_type(".") == TST_NFS_MAGIC && (errno == EOPNOTSUPP || + errno == ENOSYS)) { + tst_brk(TCONF, "fallocate system call is not implemented"); + } tst_brk(TBROK, "fallocate() failed"); + } buf = SAFE_MMAP(0, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); for (i = 0; i < (int)ARRAY_SIZE(tcases)+1; i++) - buf[i*page_size] = 0x30 + i; + buf[i*MMAP_GRANULARITY] = 0x30 + i; /* clear the page tables */ SAFE_MUNMAP(buf, mmap_size); diff --git a/testcases/kernel/syscalls/mseal/.gitignore b/testcases/kernel/syscalls/mseal/.gitignore new file mode 100644 index 00000000..2348efe3 --- /dev/null +++ b/testcases/kernel/syscalls/mseal/.gitignore @@ -0,0 +1,2 @@ +mseal01 +mseal02 diff --git a/testcases/kernel/syscalls/mseal/Makefile b/testcases/kernel/syscalls/mseal/Makefile new file mode 100644 index 00000000..35317f44 --- /dev/null +++ b/testcases/kernel/syscalls/mseal/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2023 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/mseal/mseal01.c b/testcases/kernel/syscalls/mseal/mseal01.c new file mode 100644 index 00000000..1b3e4c94 --- /dev/null +++ b/testcases/kernel/syscalls/mseal/mseal01.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This is a smoke test that verifies if mseal() protects specific VMA portions + * of a process. According to documentation, the syscall should protect memory + * from the following actions: + * + * - unmapping, moving to another location, and shrinking the size, via munmap() + * and mremap() + * - moving or expanding a different VMA into the current location, via mremap() + * - modifying a VMA via mmap(MAP_FIXED) + * - mprotect() and pkey_mprotect() + * - destructive madvice() behaviors (e.g. MADV_DONTNEED) for anonymous memory, + * when users don't have write permission to the memory + * + * Any of the described actions is recognized via EPERM errno. + * + * TODO: support both raw syscall and libc wrapper via .test_variants. + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/syscalls.h" +#include "lapi/pkey.h" + +#define MEMPAGES 8 +#define MEMSEAL 2 + +static void *mem_addr; +static int mem_size; +static int mem_offset; +static int mem_alignment; + +static inline int sys_mseal(void *start, size_t len) +{ + return tst_syscall(__NR_mseal, start, len, 0); +} + +static void test_mprotect(void) +{ + TST_EXP_FAIL(mprotect(mem_addr, mem_size, PROT_NONE), EPERM); +} + +static void test_pkey_mprotect(void) +{ + int pkey; + + check_pkey_support(); + + pkey = pkey_alloc(0, 0); + if (pkey == -1) + tst_brk(TBROK | TERRNO, "pkey_alloc failed"); + + TST_EXP_FAIL(pkey_mprotect( + mem_addr, mem_size, + PROT_NONE, + pkey), + EPERM); + + if (pkey_free(pkey) == -1) + tst_brk(TBROK | TERRNO, "pkey_free() error"); +} + +static void test_madvise(void) +{ + TST_EXP_FAIL(madvise(mem_addr, mem_size, MADV_DONTNEED), EPERM); +} + +static void test_munmap(void) +{ + TST_EXP_FAIL(munmap(mem_addr, mem_size), EPERM); +} + +static void test_mremap_resize(void) +{ + void *new_addr; + size_t new_size = 2 * mem_alignment; + + new_addr = SAFE_MMAP(NULL, mem_size, + PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0); + + TST_EXP_FAIL_PTR_VOID(mremap(mem_addr, mem_size, new_size, + MREMAP_MAYMOVE | MREMAP_FIXED, + new_addr), + EPERM); + + SAFE_MUNMAP(new_addr, new_size); +} + +static void test_mmap_change_prot(void) +{ + TST_EXP_FAIL_PTR_VOID(mmap(mem_addr, mem_size, + PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, + -1, 0), EPERM); +} + +static struct tcase { + void (*func_test)(void); + int prot; + char *message; +} tcases[] = { + {test_mprotect, PROT_READ | PROT_WRITE, "mprotect() availability"}, + {test_pkey_mprotect, PROT_READ | PROT_WRITE, "pkey_mprotect() availability"}, + {test_madvise, PROT_READ, "madvise() availability"}, + {test_munmap, PROT_READ | PROT_WRITE, "munmap() availability from child"}, + {test_mremap_resize, PROT_READ | PROT_WRITE, "mremap() address move/resize"}, + {test_mmap_change_prot, PROT_READ | PROT_WRITE, "mmap() protection change"}, +}; + +static void run(unsigned int n) +{ + /* the reason why we spawn a child is that mseal() will + * protect VMA until process will call _exit() + */ + if (!SAFE_FORK()) { + struct tcase *tc = &tcases[n]; + + mem_addr = SAFE_MMAP(NULL, mem_size, + tc->prot, + MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0); + + tst_res(TINFO, "Testing %s", tc->message); + + TST_EXP_PASS(sys_mseal(mem_addr + mem_offset, mem_alignment)); + + tc->func_test(); + _exit(0); + } +} + +static void setup(void) +{ + mem_alignment = getpagesize(); + mem_size = mem_alignment * MEMPAGES; + mem_offset = mem_alignment * MEMSEAL; +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .forks_child = 1, +}; + diff --git a/testcases/kernel/syscalls/mseal/mseal02.c b/testcases/kernel/syscalls/mseal/mseal02.c new file mode 100644 index 00000000..e11d7dbf --- /dev/null +++ b/testcases/kernel/syscalls/mseal/mseal02.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Author: Xiao Yang + */ + +/*\ + * Check various errnos for mseal(2). + * + * - mseal() fails with EINVAL if flags is invalid. + * - mseal() fails with EINVAL if the start address is not page aligned. + * - mseal() fails with EINVAL if address range overflows. + * - mseal() fails with ENOMEM if the start address is not allocated. + * - mseal() fails with ENOMEM if the end address is not allocated. + * - mseal() fails with ENOMEM if there is a gap (unallocated memory) between start and end address. + * + * TODO: support both raw syscall and libc wrapper via .test_variants. + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/syscalls.h" + +static void *start_addr, *unaligned_start_addr, *unallocated_start_addr, *unallocated_end_addr; +static size_t page_size, twopages_size, fourpages_size, overflow_size; + +static struct tcase { + void **addr; + size_t *len; + unsigned long flags; + int exp_err; +} tcases[] = { + {&start_addr, &page_size, ULONG_MAX, EINVAL}, + {&unaligned_start_addr, &page_size, 0, EINVAL}, + {&start_addr, &overflow_size, 0, EINVAL}, + {&unallocated_start_addr, &twopages_size, 0, ENOMEM}, + {&unallocated_end_addr, &twopages_size, 0, ENOMEM}, + {&start_addr, &fourpages_size, 0, ENOMEM}, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + TST_EXP_FAIL(tst_syscall(__NR_mseal, *tc->addr, *tc->len, tc->flags), tc->exp_err, + "mseal(%p, %lu, %lu)", *tc->addr, *tc->len, tc->flags); +} + +static void setup(void) +{ + page_size = getpagesize(); + twopages_size = page_size * 2; + fourpages_size = page_size * 4; + overflow_size = ULONG_MAX - page_size + 2; + start_addr = SAFE_MMAP(NULL, fourpages_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + unaligned_start_addr = start_addr + 1; + SAFE_MUNMAP(start_addr + twopages_size, page_size); + unallocated_start_addr = start_addr + twopages_size; + unallocated_end_addr = start_addr + page_size; +} + +static void cleanup(void) +{ + SAFE_MUNMAP(start_addr, fourpages_size); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/msync/msync04.c b/testcases/kernel/syscalls/msync/msync04.c index 72ddc27a..781a49dd 100755 --- a/testcases/kernel/syscalls/msync/msync04.c +++ b/testcases/kernel/syscalls/msync/msync04.c @@ -1,16 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2017 Red Hat, Inc. - * + * Copyright (c) Linux Test Project, 2017-2024 */ -/* - * Test description: Verify msync() after writing into mmap()-ed file works. +/*\ + * Verify msync() after writing into mmap()-ed file works. * * Write to mapped region and sync the memory back with file. Check the page * is no longer dirty after msync() call. + * + * In case the dirty bit is not set, check the content of file to verify + * the data is stored on disk. */ +#define _GNU_SOURCE #include #include "tst_test.h" @@ -20,7 +24,7 @@ static long pagesize; #define STRING_TO_WRITE "AAAAAAAAAA" -uint64_t get_dirty_bit(void *data) +static uint64_t get_dirty_bit(void *data) { int pagemap_fd, pageflags_fd; unsigned long addr; @@ -43,10 +47,34 @@ uint64_t get_dirty_bit(void *data) return pageflag_entry & (1ULL << 4); } +static void verify_mmaped(void) +{ + char *buffer = SAFE_MEMALIGN(getpagesize(), getpagesize()); + + tst_res(TINFO, "Haven't seen dirty bit so we check content of file instead"); + test_fd = SAFE_OPEN("msync04/testfile", O_RDONLY | O_DIRECT); + SAFE_READ(0, test_fd, buffer, getpagesize()); + + if (buffer[8] == 'B') + tst_res(TCONF, "Write was too fast, couldn't test msync()"); + else + tst_res(TFAIL, "write file failed"); + + free(buffer); +} + +static void verify_dirty(void) +{ + TST_EXP_PASS_SILENT(msync(mmaped_area, pagesize, MS_SYNC)); + + if (TST_RET == 0 && !get_dirty_bit(mmaped_area)) + tst_res(TPASS, "msync() verify dirty page ok"); + else + tst_res(TFAIL, "msync() verify dirty page failed"); +} + static void test_msync(void) { - uint64_t dirty; - test_fd = SAFE_OPEN("msync04/testfile", O_CREAT | O_TRUNC | O_RDWR, 0644); SAFE_WRITE(SAFE_WRITE_ANY, test_fd, STRING_TO_WRITE, sizeof(STRING_TO_WRITE) - 1); @@ -54,24 +82,12 @@ static void test_msync(void) MAP_SHARED, test_fd, 0); SAFE_CLOSE(test_fd); mmaped_area[8] = 'B'; - dirty = get_dirty_bit(mmaped_area); - if (!dirty) { - tst_res(TFAIL, "Expected dirty bit to be set after writing to" - " mmap()-ed area"); - goto clean; - } - if (msync(mmaped_area, pagesize, MS_SYNC) < 0) { - tst_res(TFAIL | TERRNO, "msync() failed"); - goto clean; - } - dirty = get_dirty_bit(mmaped_area); - if (dirty) - tst_res(TFAIL, "msync() failed to write dirty page despite" - " succeeding"); - else - tst_res(TPASS, "msync() working correctly"); -clean: + if (!get_dirty_bit(mmaped_area)) + verify_mmaped(); + else + verify_dirty(); + SAFE_MUNMAP(mmaped_area, pagesize); mmaped_area = NULL; } diff --git a/testcases/kernel/syscalls/munlock/munlock01.c b/testcases/kernel/syscalls/munlock/munlock01.c index 31d749e6..1ffa8c0a 100755 --- a/testcases/kernel/syscalls/munlock/munlock01.c +++ b/testcases/kernel/syscalls/munlock/munlock01.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * AUTHOR: Nirmala Devi Dhanasekar */ /*\ - * [Description] - * * Test munlock with various valid addresses and lengths. */ diff --git a/testcases/kernel/syscalls/munlock/munlock02.c b/testcases/kernel/syscalls/munlock/munlock02.c index f51c3d21..3380ab9b 100755 --- a/testcases/kernel/syscalls/munlock/munlock02.c +++ b/testcases/kernel/syscalls/munlock/munlock02.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * AUTHOR: Nirmala Devi Dhanasekar */ /*\ - * [Description] - * * Test for ENOMEM error. * * munlock(2) fails with ENOMEM if some of the specified address range diff --git a/testcases/kernel/syscalls/munlockall/munlockall01.c b/testcases/kernel/syscalls/munlockall/munlockall01.c index 51f731b6..6ec028d2 100755 --- a/testcases/kernel/syscalls/munlockall/munlockall01.c +++ b/testcases/kernel/syscalls/munlockall/munlockall01.c @@ -1,134 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) Wipro Technologies Ltd, 2002. 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. - * - * 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. - * + * Copyright Red Hat + * Author: Dennis Brendel */ -/************************************************************************** - * - * TEST IDENTIFIER : munlockall01 - * - * EXECUTED BY : root / superuser - * - * TEST TITLE : Basic test for munlockall(2) - * - * TEST CASE TOTAL : 1 - * - * AUTHOR : sowmya adiga - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * DESCRIPTION - * This is a phase I test for the munlockall(2) system call. - * It is intended to provide a limited exposure of the system call. - * - * Setup: - * Setup signal handling. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, Issue a PASS message. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * - * USAGE: - * munlockall01 [-c n] [-e] [-i n] [-I x] [-p x] [-t] - * where, -c n : Run n copies concurrently - * -e : Turn on errno logging. - * -h : Show this help screen - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -p : Pause for SIGUSR1 before starting - * -P x : Pause for x seconds between iterations. - * t : Turn on syscall timing. - * - * RESTRICTIONS - * Must be root/superuser to run it. - *****************************************************************************/ -#include + +/*\ + * Verify that munlockall(2) unlocks all previously locked memory. + */ + #include -#include "test.h" -void setup(); -void cleanup(); +#include "tst_test.h" -char *TCID = "munlockall01"; -int TST_TOTAL = 1; - -#if !defined(UCLINUX) - -int main(int ac, char **av) +static void verify_munlockall(void) { - int lc; + unsigned long size = 0; - tst_parse_opts(ac, av, NULL, NULL); + SAFE_FILE_LINES_SCANF("/proc/self/status", "VmLck: %ld", &size); - setup(); + if (size != 0UL) + tst_brk(TBROK, "Locked memory after init should be 0 but is %ld", size); - /* check looping state */ - for (lc = 0; TEST_LOOPING(lc); lc++) { + if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) + tst_brk(TBROK | TERRNO, "Could not lock memory using mlockall()"); - tst_count = 0; + SAFE_FILE_LINES_SCANF("/proc/self/status", "VmLck: %ld", &size); - TEST(munlockall()); + if (size == 0UL) + tst_brk(TBROK, "Locked memory after mlockall() should be > 0"); - /* check return code */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "munlockall() Failed with" - " return=%ld", TEST_RETURN); - } else { - tst_resm(TPASS, "munlockall() passed with" - " return=%ld ", TEST_RETURN); + TST_EXP_PASS(munlockall(), "Unlock memory using munlockall()"); - } - } - - /* cleanup and exit */ - cleanup(); - tst_exit(); + SAFE_FILE_LINES_SCANF("/proc/self/status", "VmLck: %ld", &size); + if (size != 0UL) + tst_res(TFAIL, "Locked memory after munlockall() should be 0 but is %ld", size); + else + tst_res(TPASS, "Memory successfully locked and unlocked"); } -#else - -int main(void) -{ - tst_resm(TINFO, "test is not available on uClinux"); - tst_exit(); -} - -#endif /* if !defined(UCLINUX) */ - -/* setup() - performs all ONE TIME setup for this test. */ -void setup(void) -{ - tst_require_root(); - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - -/* - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - */ -void cleanup(void) -{ -} +static struct tst_test test = { + .test_all = verify_munlockall, +}; diff --git a/testcases/kernel/syscalls/munmap/.gitignore b/testcases/kernel/syscalls/munmap/.gitignore index 94d8c10a..7ade8639 100755 --- a/testcases/kernel/syscalls/munmap/.gitignore +++ b/testcases/kernel/syscalls/munmap/.gitignore @@ -1,3 +1,3 @@ /munmap01 -/munmap02 /munmap03 +/munmap04 diff --git a/testcases/kernel/syscalls/munmap/munmap01.c b/testcases/kernel/syscalls/munmap/munmap01.c index 7d10c0ea..47b47a83 100755 --- a/testcases/kernel/syscalls/munmap/munmap01.c +++ b/testcases/kernel/syscalls/munmap/munmap01.c @@ -1,249 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: munmap01 - * - * Test Description: - * Verify that, munmap call will succeed to unmap a mapped file or - * anonymous shared memory region from the calling process's address space - * and after successful completion of munmap, the unmapped region is no - * longer accessible. - * - * Expected Result: - * munmap call should succeed to unmap a mapped file or anonymous shared - * memory region from the process's address space and it returns with a - * value 0, further reference to the unmapped region should result in a - * segmentation fault (SIGSEGV). - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * munmap01 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that, munmap call will succeed to unmap a mapped file or + * anonymous shared memory region from the calling process's address space + * and after successful completion of munmap, the unmapped region is no + * longer accessible (even if partially unmapped). * - * RESTRICTIONS: - * None.$ + * munmap call should succeed to unmap a part or all of the mapped region of + * a file or anonymous shared memory region from the process's address space + * and it returns with a value 0, further reference to the unmapped region + * should result in a segmentation fault (SIGSEGV). */ -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" -#define TEMPFILE "mmapfile" +static size_t page_sz; +static int fd = -1; +static char *map_base; +static char *map_addr; +static unsigned int map_len; +static unsigned int map_len_full; +static const char *const variants[] = { + "unmapped region not accessible", + "partially unmapped region not accessible", +}; -char *TCID = "munmap01"; -int TST_TOTAL = 1; - -char *addr; /* addr of memory mapped region */ -int fildes; /* file descriptor for tempfile */ -unsigned int map_len; /* length of the region to be mapped */ - -void setup(); /* Main setup function of test */ -void cleanup(); /* cleanup function for the test */ -void sig_handler(); /* signal catching function */ - -int main(int ac, char **av) +static void run(void) { - int lc; + int status; - tst_parse_opts(ac, av, NULL, NULL); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - setup(); - - /* - * Call munmap to unmap the mapped region of the - * temporary file from the calling process's address space. - */ - TEST(munmap(addr, map_len)); - - /* Check for the return value of munmap() */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "munmap() fails, errno=%d : %s", - TEST_ERRNO, strerror(TEST_ERRNO)); - continue; - } -#ifdef UCLINUX - /* - * No SIGSEGV on uClinux since - * MMU not implemented on uClinux - */ - tst_resm(TPASS, "call succedded"); -#else - /* - * Check whether further reference is possible - * to the unmapped memory region by writing - * to the first byte of region with - * some arbitrary number. - */ - *addr = 50; - - /* This message is printed if no SIGSEGV */ - tst_resm(TFAIL, "process succeeds to refer unmapped " - "memory region"); -#endif - - cleanup(); - - } - tst_exit(); -} - -/* - * setup() - performs all ONE TIME setup for this test. - * - * Set up signal handler to catch SIGSEGV. - * Get system page size, create a temporary file for reading/writing, - * write one byte data into it, map the open file for the specified - * map length. - */ -void setup(void) -{ - size_t page_sz; - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - /* call signal function to trap the signal generated */ - if (signal(SIGSEGV, sig_handler) == SIG_ERR) { - tst_brkm(TBROK, cleanup, "signal fails to catch signal"); - } - - TEST_PAUSE; - - /* Get the system page size */ - page_sz = getpagesize(); + SAFE_MUNMAP(map_addr, map_len); + map_base = NULL; /* - * Get the length of the open file to be mapped into process - * address space. + * Check whether further reference is possible to the unmapped memory + * region by writing to the first byte of region with some arbitrary + * number. */ - map_len = 3 * page_sz; - - tst_tmpdir(); - - /* Creat a temporary file used for mapping */ - if ((fildes = open(TEMPFILE, O_RDWR | O_CREAT, 0666)) < 0) { - tst_brkm(TBROK, cleanup, "open() on %s Failed, errno=%d : %s", - TEMPFILE, errno, strerror(errno)); + if (!SAFE_FORK()) { + *map_addr = 50; + _exit(0); } - /* - * move the file pointer to maplength position from the beginning - * of the file. - */ - SAFE_LSEEK(cleanup, fildes, map_len, SEEK_SET); - - /* Write one byte into temporary file */ - if (write(fildes, "a", 1) != 1) { - tst_brkm(TBROK, cleanup, "write() on %s Failed, errno=%d : %s", - TEMPFILE, errno, strerror(errno)); + SAFE_WAIT(&status); + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "Child was unable to access unmapped memory"); + return; } - - /* - * map the open file 'TEMPFILE' from its beginning up to the maplength - * into the calling process's address space at the system choosen - * with read/write permissions to the mapped region. - */ -#ifdef UCLINUX - /* MAP_SHARED is not implemented on uClinux */ - addr = mmap(0, map_len, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_PRIVATE, fildes, 0); -#else - addr = mmap(0, map_len, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, fildes, 0); -#endif - - /* check for the return value of mmap system call */ - if (addr == (char *)MAP_FAILED) { - tst_brkm(TBROK, cleanup, "mmap() Failed on %s, errno=%d : %s", - TEMPFILE, errno, strerror(errno)); - } - + tst_res(TFAIL, "Child succeeds to refer unmapped memory region"); } -/* - * sig_handler() - signal catching function. - * This function is used to trap the signal generated when tried to read or - * write to the memory mapped region which is already detached from the - * calling process address space. - * this function is invoked when SIGSEGV generated and it calls test - * cleanup function and exit the program. - */ -void sig_handler(void) +static void setup(void) { - tst_resm(TPASS, "Functionality of munmap() successful"); + page_sz = SAFE_SYSCONF(_SC_PAGESIZE); - /* Invoke test cleanup function and exit */ - cleanup(); + tst_res(TINFO, "Testing variant: %s", variants[tst_variant]); - tst_exit(); + map_len_full = 3 * page_sz; + map_len = map_len_full; + + fd = SAFE_OPEN("mmapfile", O_RDWR | O_CREAT, 0666); + + SAFE_LSEEK(fd, map_len, SEEK_SET); + SAFE_WRITE(SAFE_WRITE_ALL, fd, "a", 1); + + map_base = SAFE_MMAP(0, map_len, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0); + map_addr = map_base; + + if (tst_variant) { + map_addr = map_addr + page_sz; + map_len = map_len - page_sz; + } } -/* - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Close the temporary file. - * Remove the temporary directory. - */ -void cleanup(void) +static void cleanup(void) { - - /* Close the temporary file */ - if (close(fildes) < 0) { - tst_brkm(TFAIL, NULL, "close() on %s Failed, errno=%d : %s", - TEMPFILE, errno, strerror(errno)); + if (map_base) { + SAFE_MUNMAP(map_base, map_len_full); + } else { + if (tst_variant) + SAFE_MUNMAP(map_addr, page_sz); } - tst_rmdir(); + if (fd != -1) + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .forks_child = 1, + .test_variants = ARRAY_SIZE(variants), +}; diff --git a/testcases/kernel/syscalls/munmap/munmap02.c b/testcases/kernel/syscalls/munmap/munmap02.c deleted file mode 100755 index cd85d943..00000000 --- a/testcases/kernel/syscalls/munmap/munmap02.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name: munmap02 - * - * Test Description: - * Verify that, munmap call will succeed to unmap a mapped file or - * anonymous shared memory region from the calling process's address space - * if the region specified by the address and the length is part or all of - * the mapped region. - * - * Expected Result: - * munmap call should succeed to unmap a part or all of mapped region of a - * file or anonymous shared memory from the process's address space and it - * returns with a value 0, - * further reference to the unmapped region should result in a segmentation - * fault (SIGSEGV). - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * munmap01 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS: - * None. - */ -#include -#include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" - -#define TEMPFILE "mmapfile" - -char *TCID = "munmap02"; -int TST_TOTAL = 1; - -static size_t page_sz; -char *addr; /* addr of memory mapped region */ -int fildes; /* file descriptor for tempfile */ -unsigned int map_len; /* length of the region to be mapped */ - -void setup(); /* Main setup function of test */ -void cleanup(); /* cleanup function for the test */ -void sig_handler(); /* signal catching function */ - -#ifndef UCLINUX - -int main(int ac, char **av) -{ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - setup(); - - /* - * Call munmap to unmap the part of the mapped region of the - * temporary file from the address and length that is part of - * the mapped region. - */ - TEST(munmap(addr, map_len)); - - /* Check for the return value of munmap() */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "munmap() fails, errno=%d : %s", - TEST_ERRNO, strerror(TEST_ERRNO)); - continue; - } - /* - * Check whether further reference is possible - * to the unmapped memory region by writing - * to the first byte of region with - * some arbitrary number. - */ - *addr = 50; - - /* This message is printed if no SIGSEGV */ - tst_resm(TFAIL, "process succeeds to refer unmapped " - "memory region"); - cleanup(); - - } - tst_exit(); -} - -#else - -int main(void) -{ - tst_resm(TINFO, "munmap02 test is not available on uClinux"); - tst_exit(); -} - -#endif /* ifndef UCLINUX */ - -/* - * setup() - performs all ONE TIME setup for this test. - * Setup signal handler to catch SIGSEGV. - * Get system page size, create a temporary file for reading/writing, - * write one byte data into it, map the open file for the specified - * map length. - */ -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - /* call signal function to trap the signal generated */ - if (signal(SIGSEGV, sig_handler) == SIG_ERR) { - tst_brkm(TBROK, cleanup, "signal fails to catch signal"); - } - - TEST_PAUSE; - - /* Get the system page size */ - page_sz = getpagesize(); - - /* - * Get the length of the open file to be mapped into process - * address space. - */ - map_len = 3 * page_sz; - - tst_tmpdir(); - - /* Creat a temporary file used for mapping */ - if ((fildes = open(TEMPFILE, O_RDWR | O_CREAT, 0666)) < 0) { - tst_brkm(TBROK, cleanup, "open() on %s Failed, errno=%d : %s", - TEMPFILE, errno, strerror(errno)); - } - - /* - * move the file pointer to maplength position from the beginning - * of the file. - */ - SAFE_LSEEK(cleanup, fildes, map_len, SEEK_SET); - - /* Write one byte into temporary file */ - if (write(fildes, "a", 1) != 1) { - tst_brkm(TBROK, cleanup, "write() on %s Failed, errno=%d : %s", - TEMPFILE, errno, strerror(errno)); - } - - /* - * map the open file 'TEMPFILE' from its beginning up to the maplength - * into the calling process's address space at the system choosen - * with read/write permissions to the mapped region. - */ -#ifdef UCLINUX - /* mmap() doesn't support MAP_SHARED on uClinux */ - addr = mmap(0, map_len, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_PRIVATE, fildes, 0); -#else - addr = mmap(0, map_len, PROT_READ | PROT_WRITE, - MAP_FILE | MAP_SHARED, fildes, 0); -#endif - - /* check for the return value of mmap system call */ - if (addr == (char *)MAP_FAILED) { - tst_brkm(TBROK, cleanup, "mmap() Failed on %s, errno=%d : %s", - TEMPFILE, errno, strerror(errno)); - } - - /* - * increment the start address of the region at which the file is - * mapped to a maplength of 3 times the system page size by the value - * of system page size and decrement the maplength value by the value - * of system page size. - */ - addr = (char *)((long)addr + page_sz); - map_len = map_len - page_sz; -} - -/* - * void - * sig_handler() - signal catching function. - * This function is used to trap the signal generated when tried to read or - * write to the memory mapped region which is already detached from the - * calling process address space. - * this function is invoked when SIGSEGV generated and it calls test - * cleanup function and exit the program. - */ -void sig_handler(void) -{ - tst_resm(TPASS, "Functionality of munmap() successful"); - - /* Invoke test cleanup function and exit */ - cleanup(); - - tst_exit(); -} - -/* - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Unmap the portion of the region of the file left unmapped. - * Close the temporary file. - * Remove the temporary directory. - */ -void cleanup(void) -{ - - /* - * get the start address and length of the portion of - * the mapped region of the file. - */ - addr = (char *)((long)addr - page_sz); - map_len = map_len - page_sz; - - /* unmap the portion of the region of the file left unmapped */ - SAFE_MUNMAP(NULL, addr, map_len); - - /* Close the temporary file */ - SAFE_CLOSE(NULL, fildes); - - tst_rmdir(); -} diff --git a/testcases/kernel/syscalls/munmap/munmap03.c b/testcases/kernel/syscalls/munmap/munmap03.c index 23875e7b..be607e76 100755 --- a/testcases/kernel/syscalls/munmap/munmap03.c +++ b/testcases/kernel/syscalls/munmap/munmap03.c @@ -1,149 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Description: - * Verify that, - * 1. munmap() fails with -1 return value and sets errno to EINVAL - * if addresses in the range [addr,addr+len) are outside the valid - * range for the address space of a process. - * 2. munmap() fails with -1 return value and sets errno to EINVAL - * if the len argument is 0. - * 3. munmap() fails with -1 return value and sets errno to EINVAL - * if the addr argument is not a multiple of the page size as - * returned by sysconf(). - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière */ -#include -#include -#include -#include -#include -#include +/*\ + * Verify that, munmap() fails with errno: + * + * - EINVAL, if addresses in the range [addr,addr+len) are outside the valid + * range for the address space of a process. + * - EINVAL, if the len argument is 0. + * - EINVAL, if the addr argument is not a multiple of the page size as + * returned by sysconf(). + */ -#include "test.h" -#include "safe_macros.h" - -char *TCID = "munmap03"; +#include "tst_test.h" static size_t page_sz; -static char *global_addr; -static size_t global_maplen; +static char *map_addr; +static char *map_addr_out; +static size_t map_len; +static size_t map_len_zero; -static void setup(void); -static void cleanup(void); +static struct tcase { + int exp_errno; + char **addr; + size_t *len; +} tcases[] = { + { EINVAL, &map_addr_out, &map_len }, + { EINVAL, &map_addr, &map_len_zero }, + { EINVAL, &map_addr + 1, &map_len }, +}; -static void test_einval1(void); -static void test_einval2(void); -static void test_einval3(void); -static void (*testfunc[])(void) = { test_einval1, test_einval2, test_einval3 }; -int TST_TOTAL = ARRAY_SIZE(testfunc); - -int main(int ac, char **av) +static void run(unsigned int i) { - int i, lc; + struct tcase *tc = &tcases[i]; - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; i < TST_TOTAL; i++) - (*testfunc[i])(); - } - - cleanup(); - tst_exit(); + TST_EXP_FAIL(munmap(tc->addr, *tc->len), tc->exp_errno); } static void setup(void) -{ - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - page_sz = (size_t)sysconf(_SC_PAGESIZE); - - global_maplen = page_sz * 2; - global_addr = SAFE_MMAP(cleanup, NULL, global_maplen, PROT_READ | - PROT_WRITE, MAP_PRIVATE_EXCEPT_UCLINUX | - MAP_ANONYMOUS, -1, 0); -} - -static void check_and_print(int expected_errno) -{ - if (TEST_RETURN == -1) { - if (TEST_ERRNO == expected_errno) { - tst_resm(TPASS | TTERRNO, "failed as expected"); - } else { - tst_resm(TFAIL | TTERRNO, - "failed unexpectedly; expected - %d : %s", - expected_errno, strerror(expected_errno)); - } - } else { - tst_resm(TFAIL, "munmap succeeded unexpectedly"); - } -} - -static void test_einval1(void) { struct rlimit brkval; - char *addr; - size_t map_len; - SAFE_GETRLIMIT(cleanup, RLIMIT_DATA, &brkval); - - addr = (char *)brkval.rlim_max; + page_sz = SAFE_SYSCONF(_SC_PAGESIZE); map_len = page_sz * 2; + map_addr = SAFE_MMAP(NULL, map_len, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - TEST(munmap(addr, map_len)); - - check_and_print(EINVAL); -} - -static void test_einval2(void) -{ - char *addr = global_addr; - size_t map_len = 0; - - TEST(munmap(addr, map_len)); - - check_and_print(EINVAL); -} - -static void test_einval3(void) -{ - char *addr = (char *)(global_addr + 1); - size_t map_len = page_sz; - - TEST(munmap(addr, map_len)); - - check_and_print(EINVAL); + SAFE_GETRLIMIT(RLIMIT_DATA, &brkval); + map_addr_out = (char *)brkval.rlim_max; } static void cleanup(void) { - if (munmap(global_addr, global_maplen) == -1) - tst_resm(TWARN | TERRNO, "munmap failed"); + if (map_addr) + SAFE_MUNMAP(map_addr, map_len); } + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/munmap/munmap04.c b/testcases/kernel/syscalls/munmap/munmap04.c new file mode 100644 index 00000000..e1b67aee --- /dev/null +++ b/testcases/kernel/syscalls/munmap/munmap04.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Verify that munmap() fails with ENOMEM after partially unmapping an + * existing map, while having the maximum amount of maps already allocated. + */ + +#include "tst_test.h" +#include "lapi/mmap.h" + +#define PAD 2 /* avoid adjacent mapping merges */ +#define MEMSIZE 3 +#define MAP_MAX_COUNT 65530 + +static uintptr_t base = 0x10000000UL; +static size_t page_sz; +static unsigned long vma_size; +static int map_count; +static void **maps; + +static void run(void) +{ + TST_EXP_FAIL(munmap(maps[2] + page_sz, page_sz), ENOMEM); +} + +static void setup(void) +{ + uintptr_t addr = base; + + page_sz = SAFE_SYSCONF(_SC_PAGESIZE); + vma_size = MEMSIZE * page_sz; + + maps = SAFE_MALLOC(MAP_MAX_COUNT * sizeof(char *)); + for (int i = 0; i < MAP_MAX_COUNT; i++) + maps[i] = NULL; + + while (1) { + void *p = mmap((void *) addr, + vma_size, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, + -1, 0); + if (p == MAP_FAILED && errno == EEXIST) + goto next_addr; + if (p == MAP_FAILED) + break; + maps[map_count++] = p; +next_addr: + addr += PAD * vma_size; + } + + if (map_count == MAP_MAX_COUNT) + tst_brk(TBROK, "Mapped all %d regions, expected less", map_count); + if (map_count == 0) + tst_brk(TBROK, "Mapped 0 regions"); + + tst_res(TINFO, "Mapped %d regions", map_count); +} + +static void cleanup(void) +{ + for (int i = 0; i < map_count; i++) { + if (maps[i] == NULL) + break; + SAFE_MUNMAP((void *)(maps[i]), vma_size); + } + free(maps); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .min_kver = "4.17", + .save_restore = (const struct tst_path_val[]){ + { "/proc/sys/vm/max_map_count", TST_TO_STR(MAP_MAX_COUNT), TST_SR_SKIP }, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at01.c b/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at01.c index 6535fdf5..db6951e1 100755 --- a/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at01.c +++ b/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic name_to_handle_at() tests. * * [Algorithm] diff --git a/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at02.c b/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at02.c index afd9cd06..53e26f2a 100755 --- a/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at02.c +++ b/testcases/kernel/syscalls/name_to_handle_at/name_to_handle_at02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Failure tests for name_to_handle_at(). */ diff --git a/testcases/kernel/syscalls/nanosleep/nanosleep01.c b/testcases/kernel/syscalls/nanosleep/nanosleep01.c index eaacb89f..d3c29db9 100755 --- a/testcases/kernel/syscalls/nanosleep/nanosleep01.c +++ b/testcases/kernel/syscalls/nanosleep/nanosleep01.c @@ -5,10 +5,9 @@ * Copyright (C) 2015-2017 Cyril Hrubis */ -/* - * Test Description: - * nanosleep() should return with value 0 and the process should be - * suspended for time specified by timespec structure. +/*\ + * Verify that nanosleep() should return with value 0 and the process should be + * suspended for time specified by timespec structure. */ #include diff --git a/testcases/kernel/syscalls/nanosleep/nanosleep04.c b/testcases/kernel/syscalls/nanosleep/nanosleep04.c index 4eed9d0c..9308621b 100755 --- a/testcases/kernel/syscalls/nanosleep/nanosleep04.c +++ b/testcases/kernel/syscalls/nanosleep/nanosleep04.c @@ -7,13 +7,9 @@ * 07/2019 Yang Xu */ -/* - * Test Description: - * Verify that nanosleep() will fail to suspend the execution - * of a process if the specified pause time is invalid. - * - * Expected Result: - * nanosleep() should return with -1 value and sets errno to EINVAL. +/*\ + * Verify that nanosleep() returns -1 and sets errno to EINVAL when failing to suspend the + * execution of a process if the specified pause time is invalid. */ #include diff --git a/testcases/kernel/syscalls/nice/nice01.c b/testcases/kernel/syscalls/nice/nice01.c index bc58ad14..94f25f3b 100755 --- a/testcases/kernel/syscalls/nice/nice01.c +++ b/testcases/kernel/syscalls/nice/nice01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that root can provide a negative value to nice() system call and hence * root can decrease the nice value of the process using nice(). */ diff --git a/testcases/kernel/syscalls/nice/nice02.c b/testcases/kernel/syscalls/nice/nice02.c index b08e1d75..01961690 100755 --- a/testcases/kernel/syscalls/nice/nice02.c +++ b/testcases/kernel/syscalls/nice/nice02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that any user can successfully increase the nice value of * the process by passing a higher increment value (> max. applicable limits) * to nice() system call. diff --git a/testcases/kernel/syscalls/nice/nice03.c b/testcases/kernel/syscalls/nice/nice03.c index 061592e6..7f642e0b 100755 --- a/testcases/kernel/syscalls/nice/nice03.c +++ b/testcases/kernel/syscalls/nice/nice03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that any user can successfully increase the nice value of * the process by passing an increment value (< max. applicable limits) to * nice() system call. diff --git a/testcases/kernel/syscalls/nice/nice04.c b/testcases/kernel/syscalls/nice/nice04.c index ac156008..052eed8d 100755 --- a/testcases/kernel/syscalls/nice/nice04.c +++ b/testcases/kernel/syscalls/nice/nice04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, nice(2) fails when, a non-root user attempts to increase * the priority of a process by specifying a negative increment value. */ diff --git a/testcases/kernel/syscalls/nice/nice05.c b/testcases/kernel/syscalls/nice/nice05.c index 2c8ae415..b4d89655 100644 --- a/testcases/kernel/syscalls/nice/nice05.c +++ b/testcases/kernel/syscalls/nice/nice05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * 1. Create a high nice thread and a low nice thread, the main * thread wake them at the same time * 2. Both threads run on the same CPU @@ -154,5 +152,5 @@ static struct tst_test test = { .setup = setup, .test_all = verify_nice, .needs_root = 1, - .max_runtime = 3, + .runtime = 3, }; diff --git a/testcases/kernel/syscalls/open/.gitignore b/testcases/kernel/syscalls/open/.gitignore index 001d874d..af599757 100755 --- a/testcases/kernel/syscalls/open/.gitignore +++ b/testcases/kernel/syscalls/open/.gitignore @@ -12,3 +12,4 @@ /open12_child /open13 /open14 +/open15 diff --git a/testcases/kernel/syscalls/open/open02.c b/testcases/kernel/syscalls/open/open02.c index 3c9cc7e1..e1c5cc5d 100755 --- a/testcases/kernel/syscalls/open/open02.c +++ b/testcases/kernel/syscalls/open/open02.c @@ -6,11 +6,9 @@ */ /*\ - * [Description] - * * 1. open a new file without O_CREAT, ENOENT should be returned. * 2. open a file with O_RDONLY | O_NOATIME and the caller was not - * privileged, EPERM should be returned. + * privileged, EPERM should be returned. */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/open/open03.c b/testcases/kernel/syscalls/open/open03.c index 5521dfef..275ca84f 100755 --- a/testcases/kernel/syscalls/open/open03.c +++ b/testcases/kernel/syscalls/open/open03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Testcase to check open() with O_RDWR | O_CREAT. */ diff --git a/testcases/kernel/syscalls/open/open04.c b/testcases/kernel/syscalls/open/open04.c index 24ef52a2..3dc3486d 100755 --- a/testcases/kernel/syscalls/open/open04.c +++ b/testcases/kernel/syscalls/open/open04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that open(2) fails with EMFILE when per-process limit on the number * of open file descriptors has been reached. */ diff --git a/testcases/kernel/syscalls/open/open06.c b/testcases/kernel/syscalls/open/open06.c index e167c2b6..7b5bd624 100755 --- a/testcases/kernel/syscalls/open/open06.c +++ b/testcases/kernel/syscalls/open/open06.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that open(2) fails with ENXIO when * O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, * and no process has the FIFO open for reading. diff --git a/testcases/kernel/syscalls/open/open07.c b/testcases/kernel/syscalls/open/open07.c index f36db00c..a03d7511 100755 --- a/testcases/kernel/syscalls/open/open07.c +++ b/testcases/kernel/syscalls/open/open07.c @@ -1,249 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ - -/* - * NAME - * open07.c - * - * DESCRIPTION - * Test the open(2) system call to ensure that it sets ELOOP correctly. - * - * CALLS - * open() - * - * ALGORITHM - * 1. Create a symbolic link to a file, and call open(O_NOFOLLOW). Check - * that it returns ELOOP. - * - * 2. Create a symbolic link to a directory, and call open(O_NOFOLLOW). - * Check that it returns ELOOP. - * - * 3. Create a symbolic link to a symbolic link, and call open(O_NOFOLLOW). - * Check that it returns ELOOP. - * - * 4. Create a symbolic link to a symbolically linked directory, and call - * open(O_NOFOLLOW). Check that it returns ELOOP. - * - * 5. Create a symbolic link to a directory, and call - * open("link/", O_NOFOLLOW). Check that it succeeds. - * - * USAGE: - * open07 [-c n] [-e] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY + * Copyright (c) International Business Machines Corp., 2001 * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * None + * Copyright (c) 2024 SUSE LLC */ -#define _GNU_SOURCE /* for O_NOFOLLOW */ -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" -static void setup(void); -static void cleanup(void); -static void setupfunc_test1(); -static void setupfunc_test2(); -static void setupfunc_test3(); -static void setupfunc_test4(); -static void setupfunc_test5(); +/*\ + * Test functionality and error conditions of open(O_NOFOLLOW) system call. + */ -char *TCID = "open07"; -int TST_TOTAL = 5; +#include "tst_test.h" +#include "tst_safe_macros.h" -static int fd1, fd2; +#define TESTFILE "testfile" +#define TESTDIR "testdir" +#define SYMFILE1 "symfile1" +#define SYMFILE2 "symfile2" +#define SYMDIR1 "symdir1" +#define SYMDIR2 "symdir2" +#define PASSFILE "symdir1/testfile" -static struct test_case_t { - char *desc; - char filename[100]; - int flags; - int mode; - void (*setupfunc) (); - int exp_errno; -} TC[] = { - {"Test for ELOOP on f2: f1 -> f2", {}, - O_NOFOLLOW, 00700, setupfunc_test1, ELOOP}, - {"Test for ELOOP on d2: d1 -> d2", {}, - O_NOFOLLOW, 00700, setupfunc_test2, ELOOP}, - {"Test for ELOOP on f3: f1 -> f2 -> f3", {}, - O_NOFOLLOW, 00700, setupfunc_test3, ELOOP}, - {"Test for ELOOP on d3: d1 -> d2 -> d3", {}, - O_NOFOLLOW, 00700, setupfunc_test4, ELOOP}, - {"Test for success on d2: d1 -> d2", {}, - O_NOFOLLOW, 00700, setupfunc_test5, 0}, - {NULL, {}, 0, 0, NULL, 0} +static struct testcase { + const char *path; + int err; + const char *desc; +} testcase_list[] = { + {SYMFILE1, ELOOP, "open(O_NOFOLLOW) a symlink to file"}, + {SYMFILE2, ELOOP, "open(O_NOFOLLOW) a double symlink to file"}, + {SYMDIR1, ELOOP, "open(O_NOFOLLOW) a symlink to directory"}, + {SYMDIR2, ELOOP, "open(O_NOFOLLOW) a double symlink to directory"}, + {PASSFILE, 0, "open(O_NOFOLLOW) a file in symlinked directory"}, }; -int main(int ac, char **av) -{ - int lc; - int i; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - /* run the setup routines for the individual tests */ - for (i = 0; i < TST_TOTAL; i++) { - if (TC[i].setupfunc != NULL) - TC[i].setupfunc(); - } - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; TC[i].desc != NULL; ++i) { - TEST(open(TC[i].filename, TC[i].flags, TC[i].mode)); - - if (TC[i].exp_errno != 0) { - if (TEST_RETURN != -1) { - tst_resm(TFAIL, "open succeeded " - "unexpectedly"); - } - - if (TEST_ERRNO != TC[i].exp_errno) { - tst_resm(TFAIL, "open returned " - "unexpected errno, expected: " - "%d, got: %d", - TC[i].exp_errno, TEST_ERRNO); - } else { - tst_resm(TPASS, "open returned " - "expected ELOOP error"); - } - } else { - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "open failed " - "unexpectedly with errno %d", - TEST_ERRNO); - } else { - tst_resm(TPASS, "open succeeded as " - "expected"); - } - } - - if (TEST_RETURN != -1) - close(TEST_RETURN); - } - } - - cleanup(); - tst_exit(); -} - -static void setupfunc_test1(void) -{ - char file1[100], file2[100]; - - sprintf(file1, "open03.1.%d", getpid()); - sprintf(file2, "open03.2.%d", getpid()); - fd1 = SAFE_CREAT(cleanup, file1, 00700); - - SAFE_SYMLINK(cleanup, file1, file2); - - strcpy(TC[0].filename, file2); -} - -static void setupfunc_test2(void) -{ - char file1[100], file2[100]; - - sprintf(file1, "open03.3.%d", getpid()); - sprintf(file2, "open03.4.%d", getpid()); - SAFE_MKDIR(cleanup, file1, 00700); - - SAFE_SYMLINK(cleanup, file1, file2); - - strcpy(TC[1].filename, file2); -} - -static void setupfunc_test3(void) -{ - char file1[100], file2[100], file3[100]; - - sprintf(file1, "open03.5.%d", getpid()); - sprintf(file2, "open03.6.%d", getpid()); - sprintf(file3, "open03.7.%d", getpid()); - fd2 = SAFE_CREAT(cleanup, file1, 00700); - - SAFE_SYMLINK(cleanup, file1, file2); - - SAFE_SYMLINK(cleanup, file2, file3); - - strcpy(TC[2].filename, file3); -} - -static void setupfunc_test4(void) -{ - char file1[100], file2[100], file3[100]; - - sprintf(file1, "open03.8.%d", getpid()); - sprintf(file2, "open03.9.%d", getpid()); - sprintf(file3, "open03.10.%d", getpid()); - SAFE_MKDIR(cleanup, file1, 00700); - - SAFE_SYMLINK(cleanup, file1, file2); - - SAFE_SYMLINK(cleanup, file2, file3); - - strcpy(TC[3].filename, file3); -} - -static void setupfunc_test5(void) -{ - char file1[100], file2[100]; - - sprintf(file1, "open11.3.%d", getpid()); - sprintf(file2, "open12.4.%d", getpid()); - SAFE_MKDIR(cleanup, file1, 00700); - - SAFE_SYMLINK(cleanup, file1, file2); - - strcpy(TC[4].filename, file2); - strcat(TC[4].filename, "/"); -} - static void setup(void) { + int fd; + umask(0); + fd = SAFE_CREAT(TESTFILE, 0644); + SAFE_CLOSE(fd); + SAFE_MKDIR(TESTDIR, 0755); - tst_sig(NOFORK, DEF_HANDLER, cleanup); + SAFE_SYMLINK(TESTFILE, SYMFILE1); + SAFE_SYMLINK(SYMFILE1, SYMFILE2); + SAFE_SYMLINK(TESTDIR, SYMDIR1); + SAFE_SYMLINK(SYMDIR1, SYMDIR2); - TEST_PAUSE; - - tst_tmpdir(); + fd = SAFE_CREAT(PASSFILE, 0644); + SAFE_CLOSE(fd); } -static void cleanup(void) +static void run(unsigned int n) { - close(fd1); - close(fd2); + const struct testcase *tc = testcase_list + n; - tst_rmdir(); + if (tc->err) { + TST_EXP_FAIL2(open(tc->path, O_NOFOLLOW | O_RDONLY), tc->err, + "%s", tc->desc); + } else { + TST_EXP_FD(open(tc->path, O_NOFOLLOW | O_RDONLY), + "%s", tc->desc); + } + + if (TST_RET >= 0) + SAFE_CLOSE(TST_RET); } + +static struct tst_test test = { + .setup = setup, + .test = run, + .tcnt = ARRAY_SIZE(testcase_list), + .needs_tmpdir = 1 +}; diff --git a/testcases/kernel/syscalls/open/open08.c b/testcases/kernel/syscalls/open/open08.c index 0af455b5..a4906815 100755 --- a/testcases/kernel/syscalls/open/open08.c +++ b/testcases/kernel/syscalls/open/open08.c @@ -5,13 +5,11 @@ */ /*\ - * [Description] - * * Verify that open() fails with: * * - EEXIST when pathname already exists and O_CREAT and O_EXCL were used * - EISDIR when pathname refers to a directory and the access requested - * involved writing + * involved writing * - ENOTDIR when O_DIRECTORY was specified and pathname was not a directory * - ENAMETOOLONG when pathname was too long * - EACCES when requested access to the file is not allowed diff --git a/testcases/kernel/syscalls/open/open09.c b/testcases/kernel/syscalls/open/open09.c index 331ce0f6..36158b3c 100755 --- a/testcases/kernel/syscalls/open/open09.c +++ b/testcases/kernel/syscalls/open/open09.c @@ -1,109 +1,43 @@ -/* - * Copyright (c) International Business Machines Corp., 2002 - * Copyright (c) 2013 Wanlong Gao - * - * 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 - */ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Description: - * 1. open an O_WRONLY file, test if read failed. - * 2. open an O_RDONLY file, test if write failed. + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2013 Wanlong Gao + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -#include -#include -#include -#include -#include "test.h" +/*\ + * This test verifies that a file opened with O_RDONLY can't be writable + * and it verifies that a file opened with O_WRONLY can't be readable. + */ -char *TCID = "open09"; -int TST_TOTAL = 2; +#include "tst_test.h" -#define PASSED 1 -#define FAILED 0 - -static char tempfile[40] = ""; - -static void setup(void); -static void cleanup(void); - -int main(int ac, char *av[]) -{ - int fildes; - int ret = 0; - char pbuf[BUFSIZ]; - - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - fildes = open(tempfile, O_WRONLY); - if (fildes == -1) - tst_brkm(TFAIL, cleanup, "\t\t\topen failed"); - - ret = read(fildes, pbuf, 1); - if (ret != -1) - tst_resm(TFAIL, "read should not succeed"); - else - tst_resm(TPASS, "Test passed in O_WRONLY."); - - close(fildes); - - fildes = open(tempfile, O_RDONLY); - if (fildes == -1) { - tst_resm(TFAIL, "\t\t\topen failed"); - } else { - ret = write(fildes, pbuf, 1); - if (ret != -1) - tst_resm(TFAIL, "write should not succeed"); - else - tst_resm(TPASS, "Test passed in O_RDONLY."); - } - close(fildes); - } - - cleanup(); - tst_exit(); -} +#define TEMPFILE "testfile" static void setup(void) { - int fildes; - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - TEST_PAUSE; - tst_tmpdir(); - - sprintf(tempfile, "open09.%d", getpid()); - - fildes = creat(tempfile, 0600); - if (fildes == -1) { - tst_brkm(TBROK, cleanup, "\t\t\tcan't create '%s'", - tempfile); - } else { - close(fildes); - } + SAFE_CREAT(TEMPFILE, 0600); } -static void cleanup(void) +static void verify_open(unsigned int nr) { - unlink(tempfile); - tst_rmdir(); + char pbuf[BUFSIZ]; + int fd = 0; + + if (nr == 0) { + fd = SAFE_OPEN(TEMPFILE, O_RDONLY); + TST_EXP_FAIL(write(fd, pbuf, 1), EBADF); + } else { + fd = SAFE_OPEN(TEMPFILE, O_WRONLY); + TST_EXP_FAIL(read(fd, pbuf, 1), EBADF); + } + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .setup = setup, + .test = verify_open, + .tcnt = 2, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/open/open10.c b/testcases/kernel/syscalls/open/open10.c index d2d3729d..5e27a75d 100755 --- a/testcases/kernel/syscalls/open/open10.c +++ b/testcases/kernel/syscalls/open/open10.c @@ -4,8 +4,6 @@ * Copyright (c) 2021 SUSE LLC */ /*\ - * [Description] - * * Verify that the group ID and setgid bit are set correctly when a new file * is created. */ @@ -27,7 +25,6 @@ #define NOSETGID_B DIR_B "/nosetgid" #define ROOT_SETGID DIR_B "/root_setgid" -static char *tmpdir; static uid_t orig_uid, nobody_uid; static gid_t nobody_gid, free_gid; static int fd = -1; @@ -42,7 +39,6 @@ static void setup(void) tst_res(TINFO, "User nobody: uid = %d, gid = %d", (int)nobody_uid, (int)nobody_gid); free_gid = tst_get_free_gid(nobody_gid); - tmpdir = tst_get_tmpdir(); } static void file_test(const char *name, mode_t mode, int sgid, gid_t gid) @@ -123,15 +119,13 @@ static void run(void) file_test(ROOT_SETGID, MODE_SGID, 1, free_gid); /* Cleanup between loops */ - tst_purge_dir(tmpdir); + tst_purge_dir(tst_tmpdir_path()); } static void cleanup(void) { if (fd >= 0) SAFE_CLOSE(fd); - - free(tmpdir); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/open/open11.c b/testcases/kernel/syscalls/open/open11.c index 3c3c11b8..6f89d558 100755 --- a/testcases/kernel/syscalls/open/open11.c +++ b/testcases/kernel/syscalls/open/open11.c @@ -1,28 +1,32 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2013 Red Hat, Inc. - * + * Copyright (c) Linux Test Project, 2013-2022 + */ + +/*\ * Basic tests for open(2) and make sure open(2) works and handles error * conditions correctly. * * There are 28 test cases: + * * 1. Open regular file O_RDONLY * 2. Open regular file O_WRONLY * 3. Open regular file O_RDWR * 4. Open regular file O_RDWR | O_SYNC * 5. Open regular file O_RDWR | O_TRUNC - * 6. Open dir O_RDONLY - * 7. Open dir O_RDWR, expect EISDIR + * 6. Open directory O_RDONLY + * 7. Open directory O_RDWR, expect EISDIR * 8. Open regular file O_DIRECTORY, expect ENOTDIR * 9. Open hard link file O_RDONLY * 10. Open hard link file O_WRONLY * 11. Open hard link file O_RDWR - * 12. Open sym link file O_RDONLY - * 13. Open sym link file O_WRONLY - * 14. Open sym link file O_RDWR - * 15. Open sym link dir O_RDONLY - * 16. Open sym link dir O_WRONLY, expect EISDIR - * 17. Open sym link dir O_RDWR, expect EISDIR + * 12. Open symlink file O_RDONLY + * 13. Open symlink file O_WRONLY + * 14. Open symlink file O_RDWR + * 15. Open symlink directory O_RDONLY + * 16. Open symlink directory O_WRONLY, expect EISDIR + * 17. Open symlink directory O_RDWR, expect EISDIR * 18. Open device special file O_RDONLY * 19. Open device special file O_WRONLY * 20. Open device special file O_RDWR @@ -30,8 +34,8 @@ * 22. Open link file O_RDONLY | O_CREAT * 23. Open symlink file O_RDONLY | O_CREAT * 24. Open regular file O_RDONLY | O_CREAT - * 25. Open symlink dir O_RDONLY | O_CREAT, expect EISDIR - * 26. Open dir O_RDONLY | O_CREAT, expect EISDIR + * 25. Open symlink directory O_RDONLY | O_CREAT, expect EISDIR + * 26. Open directory O_RDONLY | O_CREAT, expect EISDIR * 27. Open regular file O_RDONLY | O_TRUNC, behaviour is undefined but should * not oops or hang * 28. Open regular file(non-empty) O_RDONLY | O_TRUNC, behaviour is undefined @@ -54,7 +58,7 @@ #define T_LINK_REG "t_link_reg" /* hard link to T_REG */ #define T_NEW_REG "t_new_reg" /* new regular file to be created */ #define T_SYMLINK_REG "t_symlink_reg" /* symlink to T_REG */ -#define T_DIR "t_dir" /* test dir */ +#define T_DIR "t_dir" /* test directory */ #define T_SYMLINK_DIR "t_symlink_dir" /* symlink to T_DIR */ #define T_DEV MNTPOINT"/t_dev" /* test device special file */ @@ -68,205 +72,191 @@ static struct test_case { int err; } tc[] = { /* Test open(2) regular file */ - { /* open regular file O_RDONLY */ - .desc = "Open regular file O_RDONLY", + { + .desc = "open regular file O_RDONLY", .path = T_REG_EMPTY, .flags = O_RDONLY, .mode = 0644, - .err = 0, }, - { /* open regular file O_WRONLY */ - .desc = "Open regular file O_WRONLY", + { + .desc = "open regular file O_WRONLY", .path = T_REG_EMPTY, .flags = O_WRONLY, .mode = 0644, - .err = 0, }, - { /* open regular file O_RDWR */ - .desc = "Open regular file O_RDWR", + { + .desc = "open regular file O_RDWR", .path = T_REG_EMPTY, .flags = O_RDWR, .mode = 0644, - .err = 0, }, - { /* open regular file O_RDWR | O_SYNC*/ - .desc = "Open regular file O_RDWR | O_SYNC", + { + .desc = "open regular file O_RDWR | O_SYNC", .path = T_REG_EMPTY, .flags = O_RDWR | O_SYNC, .mode = 0644, - .err = 0, }, - { /* open regular file O_RDWR | O_TRUNC */ - .desc = "Open regular file O_RDWR | O_TRUNC", + { + .desc = "open regular file O_RDWR | O_TRUNC", .path = T_REG_EMPTY, .flags = O_RDWR | O_TRUNC, .mode = 0644, - .err = 0, }, + /* Test open(2) directory */ - { /* open dir O_RDONLY */ - .desc = "Open dir O_RDONLY", + { + .desc = "open directory O_RDONLY", .path = T_DIR, .flags = O_RDONLY, .mode = 0755, - .err = 0, }, - { /* open dir O_RDWR */ - .desc = "Open dir O_RDWR, expect EISDIR", + { + .desc = "open directory O_RDWR", .path = T_DIR, .flags = O_RDWR, .mode = 0755, .err = EISDIR, }, - { /* open regular file O_DIRECTORY */ - .desc = "Open regular file O_DIRECTORY, expect ENOTDIR", + { + .desc = "open regular file O_DIRECTORY", .path = T_REG_EMPTY, .flags = O_RDONLY | O_DIRECTORY, .mode = 0644, .err = ENOTDIR, }, /* Test open(2) hard link */ - { /* open hard link file O_RDONLY */ - .desc = "Open hard link file O_RDONLY", + { + .desc = "open hard link file O_RDONLY", .path = T_LINK_REG, .flags = O_RDONLY, .mode = 0644, - .err = 0, }, - { /* open hard link file O_WRONLY */ - .desc = "Open hard link file O_WRONLY", + { + .desc = "open hard link file O_WRONLY", .path = T_LINK_REG, .flags = O_WRONLY, .mode = 0644, - .err = 0, }, - { /* open hard link file O_RDWR */ - .desc = "Open hard link file O_RDWR", + { + .desc = "open hard link file O_RDWR", .path = T_LINK_REG, .flags = O_RDWR, .mode = 0644, - .err = 0, }, - /* Test open(2) sym link */ - { /* open sym link file O_RDONLY */ - .desc = "Open sym link file O_RDONLY", + + /* Test open(2) symlink */ + { + .desc = "open symlink file O_RDONLY", .path = T_SYMLINK_REG, .flags = O_RDONLY, .mode = 0644, - .err = 0, }, - { /* open sym link file O_WRONLY */ - .desc = "Open sym link file O_WRONLY", + { + .desc = "open symlink file O_WRONLY", .path = T_SYMLINK_REG, .flags = O_WRONLY, .mode = 0644, - .err = 0, }, - { /* open sym link file O_RDWR */ - .desc = "Open sym link file O_RDWR", + { + .desc = "open symlink file O_RDWR", .path = T_SYMLINK_REG, .flags = O_RDWR, .mode = 0644, - .err = 0, }, - { /* open sym link dir O_RDONLY */ - .desc = "Open sym link dir O_RDONLY", + { + .desc = "open symlink directory O_RDONLY", .path = T_SYMLINK_DIR, .flags = O_RDONLY, .mode = 0644, - .err = 0, }, - { /* open sym link dir O_WRONLY */ - .desc = "Open sym link dir O_WRONLY, expect EISDIR", + { + .desc = "open symlink directory O_WRONLY", .path = T_SYMLINK_DIR, .flags = O_WRONLY, .mode = 0644, .err = EISDIR, }, - { /* open sym link dir O_RDWR */ - .desc = "Open sym link dir O_RDWR, expect EISDIR", + { + .desc = "open symlink directory O_RDWR", .path = T_SYMLINK_DIR, .flags = O_RDWR, .mode = 0644, .err = EISDIR, }, - /* * Test open(2) device special */ - { /* open device special file O_RDONLY */ - .desc = "Open device special file O_RDONLY", + + /* Test open(2) device special */ + { + .desc = "open device special file O_RDONLY", .path = T_DEV, .flags = O_RDONLY, .mode = 0644, - .err = 0, }, - { /* open device special file O_WRONLY */ - .desc = "Open device special file O_WRONLY", + { + .desc = "open device special file O_WRONLY", .path = T_DEV, .flags = O_WRONLY, .mode = 0644, - .err = 0, }, - { /* open device special file O_RDWR */ - .desc = "Open device special file O_RDWR", + { + .desc = "open device special file O_RDWR", .path = T_DEV, .flags = O_RDWR, .mode = 0644, - .err = 0, }, - /* * Test open(2) non-existing file */ - { /* open non-existing regular file in existing dir */ - .desc = "Open non-existing regular file in existing dir", + + /* Test open(2) non-existing file */ + { + .desc = "open non-existing regular file in existing dir", .path = T_DIR"/"T_NEW_REG, .flags = O_RDWR | O_CREAT, .mode = 0644, - .err = 0, }, + /* test open(2) with O_CREAT */ - { /* open hard link file O_RDONLY | O_CREAT */ - .desc = "Open link file O_RDONLY | O_CREAT", + { + .desc = "open link file O_RDONLY | O_CREAT", .path = T_LINK_REG, .flags = O_RDONLY | O_CREAT, .mode = 0644, - .err = 0, }, - { /* open sym link file O_RDONLY | O_CREAT */ - .desc = "Open symlink file O_RDONLY | O_CREAT", + { + .desc = "open symlink file O_RDONLY | O_CREAT", .path = T_SYMLINK_REG, .flags = O_RDONLY | O_CREAT, .mode = 0644, - .err = 0, }, - { /* open regular file O_RDONLY | O_CREAT */ - .desc = "Open regular file O_RDONLY | O_CREAT", + { + .desc = "open regular file O_RDONLY | O_CREAT", .path = T_REG_EMPTY, .flags = O_RDONLY | O_CREAT, .mode = 0644, - .err = 0, }, - { /* open symlink dir O_RDONLY | O_CREAT */ - .desc = "Open symlink dir O_RDONLY | O_CREAT, expect EISDIR", + { + .desc = "open symlink directory O_RDONLY | O_CREAT", .path = T_SYMLINK_DIR, .flags = O_RDONLY | O_CREAT, .mode = 0644, .err = EISDIR, }, - { /* open dir O_RDONLY | O_CREAT */ - .desc = "Open dir O_RDONLY | O_CREAT, expect EISDIR", + { + .desc = "open directory O_RDONLY | O_CREAT", .path = T_DIR, .flags = O_RDONLY | O_CREAT, .mode = 0644, .err = EISDIR, }, + /* Other random open(2) tests */ - { /* open regular file O_RDONLY | O_TRUNC */ - .desc = "Open regular file O_RDONLY | O_TRUNC, " + { + .desc = "open regular file O_RDONLY | O_TRUNC, " "behaviour is undefined but should not oops or hang", .path = T_REG_EMPTY, .flags = O_RDONLY | O_TRUNC, .mode = 0644, .err = -1, }, - { /* open regular(non-empty) file O_RDONLY | O_TRUNC */ - .desc = "Open regular file(non-empty) O_RDONLY | O_TRUNC, " + { + .desc = "open regular file(non-empty) O_RDONLY | O_TRUNC, " "behaviour is undefined but should not oops or hang", .path = T_REG, .flags = O_RDONLY | O_TRUNC, diff --git a/testcases/kernel/syscalls/open/open12.c b/testcases/kernel/syscalls/open/open12.c index 188d1794..515e18cb 100755 --- a/testcases/kernel/syscalls/open/open12.c +++ b/testcases/kernel/syscalls/open/open12.c @@ -1,134 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Fujitsu Ltd. * Author: Zeng Linggang - * - * 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. + * Copyright (c) 2025 SUSE LLC */ -/* - * DESCRIPTION - * This test case will verify basic function of open(2) with the flags - * O_APPEND, O_NOATIME, O_CLOEXEC and O_LARGEFILE. +/*\ + * This test case will verify basic function of open(2) with the flags + * O_APPEND, O_NOATIME, O_CLOEXEC and O_LARGEFILE. */ #define _GNU_SOURCE -#include -#include #include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" +#include "tst_safe_macros.h" #include "lapi/fcntl.h" #include "lapi/mount.h" #define MNTPOINT "mntpoint" -#define TEST_FILE MNTPOINT"/test_file" -#define LARGE_FILE "large_file" +#define TEST_FILE MNTPOINT "/test_file" +#define LARGE_FILE MNTPOINT "/large_file" -#define DIR_MODE 0755 +static int fd = -1; -char *TCID = "open12"; - -static const char *device; -static unsigned int mount_flag, skip_noatime; - -static void setup(void); -static void cleanup(void); static void test_append(void); static void test_noatime(void); static void test_cloexec(void); static void test_largefile(void); -static void (*test_func[])(void) = { test_append, test_noatime, test_cloexec, - test_largefile }; +static void (*test_func[])(void) = { + test_append, test_noatime, test_cloexec, test_largefile +}; -int TST_TOTAL = ARRAY_SIZE(test_func); - -int main(int argc, char **argv) +static void run(unsigned int n) { - int lc; - int i; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - for (i = 0; i < TST_TOTAL; i++) - (*test_func[i])(); - } - - cleanup(); - tst_exit(); + test_func[n](); } static void setup(void) { - const char *mount_flags[] = {"noatime", "relatime", NULL}; - - TEST_PAUSE; - - tst_sig(FORK, DEF_HANDLER, cleanup); - - tst_tmpdir(); - - SAFE_MKDIR(cleanup, MNTPOINT, DIR_MODE); - - if (tst_path_has_mnt_flags(cleanup, NULL, mount_flags)) { - const char *fs_type; - - fs_type = tst_dev_fs_type(); - device = tst_acquire_device(cleanup); - - if (!device) { - tst_resm(TINFO, "Failed to obtain block device"); - skip_noatime = 1; - goto end; - } - - tst_mkfs(cleanup, device, fs_type, NULL, NULL); - - SAFE_MOUNT(cleanup, device, MNTPOINT, fs_type, MS_STRICTATIME, NULL); - mount_flag = 1; - } - -end: - SAFE_FILE_PRINTF(cleanup, TEST_FILE, TEST_FILE); + SAFE_FILE_PRINTF(TEST_FILE, TEST_FILE); } static void test_append(void) { off_t len1, len2; + struct stat statbuf; - TEST(open(TEST_FILE, O_RDWR | O_APPEND, 0777)); + tst_res(TINFO, "Testing O_APPEND"); - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "open failed"); + fd = TST_EXP_FD_SILENT(open(TEST_FILE, O_RDWR | O_APPEND, 0644)); + + if (!TST_PASS) + return; + + len1 = SAFE_LSEEK(fd, 0, SEEK_CUR); + + if (len1) + tst_res(TFAIL, "Initial cursor position is not zero"); + + SAFE_FSTAT(fd, &statbuf); + len1 = strlen(TEST_FILE); + SAFE_WRITE(1, fd, TEST_FILE, len1); + len2 = SAFE_LSEEK(fd, 0, SEEK_CUR); + SAFE_CLOSE(fd); + len1 += statbuf.st_size; + + if (len2 != len1) { + tst_res(TFAIL, "Wrong cursor position after write: %ld != %ld", + (long)len2, (long)len1); return; } - len1 = SAFE_LSEEK(cleanup, TEST_RETURN, 0, SEEK_CUR); - SAFE_WRITE(cleanup, SAFE_WRITE_ALL, TEST_RETURN, TEST_FILE, - sizeof(TEST_FILE)); - len2 = SAFE_LSEEK(cleanup, TEST_RETURN, 0, SEEK_CUR); - SAFE_CLOSE(cleanup, TEST_RETURN); - - if (len2 > len1) - tst_resm(TPASS, "test O_APPEND for open success"); - else - tst_resm(TFAIL, "test O_APPEND for open failed"); + tst_res(TPASS, "O_APPEND works as expected"); } static void test_noatime(void) @@ -136,31 +80,23 @@ static void test_noatime(void) char read_buf; struct stat old_stat, new_stat; - if (skip_noatime) { - tst_resm(TCONF, - "test O_NOATIME flag for open needs filesystems which " - "is mounted without noatime and relatime"); - return; - } - - SAFE_STAT(cleanup, TEST_FILE, &old_stat); + tst_res(TINFO, "Testing O_NOATIME"); + SAFE_STAT(TEST_FILE, &old_stat); sleep(1); + fd = TST_EXP_FD_SILENT(open(TEST_FILE, O_RDONLY | O_NOATIME, 0644)); - TEST(open(TEST_FILE, O_RDONLY | O_NOATIME, 0777)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "open failed"); + if (!TST_PASS) return; - } - SAFE_READ(cleanup, 1, TEST_RETURN, &read_buf, 1); - SAFE_CLOSE(cleanup, TEST_RETURN); - SAFE_STAT(cleanup, TEST_FILE, &new_stat); + + SAFE_READ(1, fd, &read_buf, 1); + SAFE_CLOSE(fd); + SAFE_STAT(TEST_FILE, &new_stat); if (old_stat.st_atime == new_stat.st_atime) - tst_resm(TPASS, "test O_NOATIME for open success"); + tst_res(TPASS, "File access time was not modified"); else - tst_resm(TFAIL, "test O_NOATIME for open failed"); + tst_res(TFAIL, "File access time changed"); } static void test_cloexec(void) @@ -169,80 +105,92 @@ static void test_cloexec(void) int status; char buf[20]; - TEST(open(TEST_FILE, O_RDWR | O_APPEND | O_CLOEXEC, 0777)); + tst_res(TINFO, "Testing O_CLOEXEC"); - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "open failed"); + fd = TST_EXP_FD_SILENT(open(TEST_FILE, O_RDWR | O_APPEND | O_CLOEXEC, + 0644)); + + if (!TST_PASS) return; - } - sprintf(buf, "%ld", TEST_RETURN); - - pid = tst_fork(); - if (pid < 0) - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); + snprintf(buf, sizeof(buf), "%d", fd); + buf[sizeof(buf) - 1] = '\0'; + pid = SAFE_FORK(); if (pid == 0) { if (execlp("open12_child", "open12_child", buf, NULL)) exit(2); } - SAFE_CLOSE(cleanup, TEST_RETURN); + SAFE_CLOSE(fd); if (wait(&status) != pid) - tst_brkm(TBROK | TERRNO, cleanup, "wait() failed"); + tst_brk(TBROK | TERRNO, "wait() failed"); - if (WIFEXITED(status)) { - switch ((int8_t)WEXITSTATUS(status)) { - case 0: - tst_resm(TPASS, "test O_CLOEXEC for open success"); + if (!WIFEXITED(status)) + tst_brk(TBROK, "open12_child exited with unexpected error"); + + switch (WEXITSTATUS(status)) { + case 0: + tst_res(TPASS, "File descriptor was closed by execlp()"); break; - case 1: - tst_resm(TFAIL, "test O_CLOEXEC for open failed"); + case 1: + tst_res(TFAIL, "File descriptor remained open after execlp()"); break; - default: - tst_brkm(TBROK, cleanup, "execlp() failed"); - } - } else { - tst_brkm(TBROK, cleanup, - "open12_child exits with unexpected error"); + default: + tst_brk(TBROK, "execlp() failed"); } } static void test_largefile(void) { - int fd; off_t offset; - fd = SAFE_OPEN(cleanup, LARGE_FILE, - O_LARGEFILE | O_RDWR | O_CREAT, 0777); + tst_res(TINFO, "Testing O_LARGEFILE"); - offset = lseek(fd, 4.1*1024*1024*1024, SEEK_SET); - if (offset == -1) - tst_brkm(TBROK | TERRNO, cleanup, "lseek failed"); + fd = TST_EXP_FD_SILENT(open(LARGE_FILE, O_LARGEFILE | O_RDWR | O_CREAT, + 0644)); - SAFE_WRITE(cleanup, SAFE_WRITE_ALL, fd, LARGE_FILE, - sizeof(LARGE_FILE)); + if (!TST_PASS) + return; - SAFE_CLOSE(cleanup, fd); + offset = lseek(fd, 4ULL * TST_GB + TST_MB, SEEK_SET); - TEST(open(LARGE_FILE, O_LARGEFILE | O_RDONLY, 0777)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "test O_LARGEFILE for open failed"); - } else { - tst_resm(TPASS, "test O_LARGEFILE for open success"); - SAFE_CLOSE(cleanup, TEST_RETURN); + if (offset < 0) { + tst_res(TFAIL | TERRNO, "lseek() past 4GB range failed"); + return; } + + SAFE_WRITE(1, fd, LARGE_FILE, strlen(LARGE_FILE)); + SAFE_CLOSE(fd); + fd = open(LARGE_FILE, O_LARGEFILE | O_RDONLY, 0644); + + if (fd < 0) { + tst_res(TFAIL | TERRNO, "Cannot open large file again"); + return; + } + + tst_res(TPASS, "O_LARGEFILE works as expected"); + SAFE_CLOSE(fd); } static void cleanup(void) { - if (mount_flag && tst_umount(MNTPOINT) == -1) - tst_brkm(TWARN | TERRNO, NULL, "umount(2) failed"); - - if (device) - tst_release_device(device); - - tst_rmdir(); + if (fd >= 0) + SAFE_CLOSE(fd); } + +static struct tst_test test = { + .setup = setup, + .test = run, + .cleanup = cleanup, + .tcnt = ARRAY_SIZE(test_func), + .forks_child = 1, + .needs_root = 1, + .all_filesystems = 1, + .mntpoint = MNTPOINT, + .filesystems = (struct tst_fs[]){ + { .mnt_flags = MS_STRICTATIME }, + {} + } +}; diff --git a/testcases/kernel/syscalls/open/open13.c b/testcases/kernel/syscalls/open/open13.c index e777a305..aea9ec4b 100755 --- a/testcases/kernel/syscalls/open/open13.c +++ b/testcases/kernel/syscalls/open/open13.c @@ -1,163 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015 Fujitsu Ltd. * Author: Guangwen Feng - * - * 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. + * Copyright (c) 2025 SUSE LLC */ -/* - * DESCRIPTION - * Basic test for O_PATH flag of open(2). - * "Obtain a file descriptor that can be used to perform operations - * that act purely at the file descriptor level, the file itself is - * not opened, the operations read(2), write(2), fchmod(2), fchown(2) - * and fgetxattr(2) fail with the error EBADF." +/*\ + * Basic test for O_PATH flag of :man2:`open`: * - * The operations include but are not limited to the syscalls above. + * Obtain a file descriptor that can be used to perform operations + * that act purely at the file descriptor level, the file itself is + * not opened, the operations :man2:`read`, :man2:`write`, :man2:`fchmod`, + * :man2:`fchown` and :man2:`fgetxattr` fail with the error EBADF. + * + * The operations include but are not limited to the syscalls above. */ -#define _GNU_SOURCE - #include "config.h" -#include +#include +#include + #ifdef HAVE_SYS_XATTR_H #include #include #endif -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" +#include "tst_safe_macros.h" #include "lapi/fcntl.h" #define TESTFILE "testfile" -#define FILE_MODE (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID) -static void setup(void); -static void verify_read(void); -static void verify_write(void); -static void verify_fchmod(void); -static void verify_fchown(void); +static int path_fd = -1, dup_fd = -1; + +static int verify_read(int fd); +static int verify_write(int fd); +static int verify_fchmod(int fd); +static int verify_fchown(int fd); +static int verify_ioctl(int fd); +static int verify_mmap(int fd); #ifdef HAVE_SYS_XATTR_H -static void verify_fgetxattr(void); +static int verify_fgetxattr(int fd); #endif -static void check_result(const char *call_name); -static void cleanup(void); -static int fd; - -static void (*test_func[])(void) = { - verify_read, - verify_write, - verify_fchmod, - verify_fchown, +static const struct { + int (*func)(int fd); + const char *name; +} testcases[] = { + {verify_read, "read"}, + {verify_write, "write"}, + {verify_fchmod, "fchmod"}, + {verify_fchown, "fchown"}, + {verify_ioctl, "ioctl"}, + {verify_mmap, "mmap"}, #ifdef HAVE_SYS_XATTR_H - verify_fgetxattr + {verify_fgetxattr, "fgetxattr"}, #endif + {} }; -char *TCID = "open13"; -int TST_TOTAL = ARRAY_SIZE(test_func); - -int main(int ac, char **av) -{ - int lc; - int tc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - fd = SAFE_OPEN(cleanup, TESTFILE, O_RDWR | O_PATH); - - for (tc = 0; tc < TST_TOTAL; tc++) - (*test_func[tc])(); - - SAFE_CLOSE(cleanup, fd); - fd = 0; - } - - cleanup(); - tst_exit(); -} - static void setup(void) { - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - tst_tmpdir(); - - SAFE_TOUCH(cleanup, TESTFILE, FILE_MODE, NULL); - - TEST_PAUSE; + path_fd = SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT, 0644); + SAFE_CLOSE(path_fd); + path_fd = SAFE_OPEN(TESTFILE, O_PATH); + dup_fd = SAFE_DUP(path_fd); } -static void verify_read(void) +static void run(void) +{ + int i; + + for (i = 0; testcases[i].func; i++) { + TST_EXP_FAIL(testcases[i].func(path_fd), EBADF, + "%s() on original FD", testcases[i].name); + TST_EXP_FAIL(testcases[i].func(dup_fd), EBADF, + "%s() on duplicated FD", testcases[i].name); + } +} + +static int verify_read(int fd) { char buf[255]; - TEST(read(fd, buf, sizeof(buf))); - check_result("read(2)"); + return read(fd, buf, sizeof(buf)); } -static void verify_write(void) +static int verify_write(int fd) { - TEST(write(fd, "w", 1)); - check_result("write(2)"); + return write(fd, "w", 1); } -static void verify_fchmod(void) +static int verify_fchmod(int fd) { - TEST(fchmod(fd, 0666)); - check_result("fchmod(2)"); + return fchmod(fd, 0666); } -static void verify_fchown(void) +static int verify_fchown(int fd) { - TEST(fchown(fd, 1000, 1000)); - check_result("fchown(2)"); + return fchown(fd, 1000, 1000); +} + +static int verify_ioctl(int fd) +{ + int arg; + + return ioctl(fd, FIGETBSZ, &arg); +} + +static int verify_mmap(int fd) +{ + void *ptr; + + ptr = mmap(NULL, 1, PROT_READ, MAP_PRIVATE, fd, 0); + + if (ptr == MAP_FAILED) + return -1; + + SAFE_MUNMAP(ptr, 1); + return 0; } #ifdef HAVE_SYS_XATTR_H -static void verify_fgetxattr(void) +static int verify_fgetxattr(int fd) { - TEST(fgetxattr(fd, "tkey", NULL, 1)); - check_result("fgetxattr(2)"); + return fgetxattr(fd, "tkey", NULL, 0); } #endif -static void check_result(const char *call_name) -{ - if (TEST_RETURN == 0) { - tst_resm(TFAIL, "%s succeeded unexpectedly", call_name); - return; - } - - if (TEST_ERRNO != EBADF) { - tst_resm(TFAIL | TTERRNO, "%s failed unexpectedly, " - "expected EBADF", call_name); - return; - } - - tst_resm(TPASS, "%s failed with EBADF", call_name); -} - static void cleanup(void) { - if (fd > 0 && close(fd)) - tst_resm(TWARN | TERRNO, "failed to close file"); + if (path_fd >= 0) + SAFE_CLOSE(path_fd); - tst_rmdir(); + if (dup_fd >= 0) + SAFE_CLOSE(dup_fd); } + +static struct tst_test test = { + .setup = setup, + .test_all = run, + .cleanup = cleanup, + .needs_tmpdir = 1 +}; diff --git a/testcases/kernel/syscalls/open/open14.c b/testcases/kernel/syscalls/open/open14.c index 3ecb7e4f..a3c7d38a 100755 --- a/testcases/kernel/syscalls/open/open14.c +++ b/testcases/kernel/syscalls/open/open14.c @@ -1,63 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015-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 . - * * Author: Alexey Kodanev - * + * Copyright (c) 2025 SUSE LLC */ -#define _GNU_SOURCE -#include -#include -#include -#include +/*\ + * Check the functionality of O_TMPFILE flag for open() syscall: + * + * 1) Creation and linking (naming) of a single temp file + * 2) Creation of multiple unlinked temp files in a hierarchy of directories + * 3) Access permissions of linked temp files match creation mode argument + */ -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" +#include "tst_safe_macros.h" #include "lapi/fcntl.h" -char *TCID = "open14"; -int TST_TOTAL = 3; -static ssize_t size; -static char buf[1024]; -static const ssize_t blocks_num = 4; -static struct stat st; +#define FILE_COUNT 100 +#define MNTPOINT "mntpoint" -static void cleanup(void) -{ - tst_rmdir(); -} +static char buf[1024]; +static int fds[FILE_COUNT]; +static const ssize_t size = sizeof(buf); +static const ssize_t blocks_num = 4; static void setup(void) { - tst_tmpdir(); + int i; - size = sizeof(buf); + for (i = 0; i < FILE_COUNT; i++) + fds[i] = -1; memset(buf, 1, size); + SAFE_CHDIR(MNTPOINT); + fds[0] = open(".", O_TMPFILE | O_RDWR, 0600); - int fd = open(".", O_TMPFILE | O_RDWR, 0600); - - if (fd == -1) { + if (fds[0] == -1) { if (errno == EISDIR || errno == ENOTSUP) - tst_brkm(TCONF, cleanup, "O_TMPFILE not supported"); + tst_brk(TCONF, "O_TMPFILE not supported"); - tst_brkm(TBROK | TERRNO, cleanup, "open() failed"); + tst_brk(TBROK | TERRNO, "open() failed"); } - SAFE_CLOSE(cleanup, fd); + SAFE_CLOSE(fds[0]); } static void write_file(int fd) @@ -65,183 +51,188 @@ static void write_file(int fd) int i; for (i = 0; i < blocks_num; ++i) - SAFE_WRITE(cleanup, SAFE_WRITE_ALL, fd, buf, size); + SAFE_WRITE(1, fd, buf, size); } -void test01(void) +static void test01(void) { - int fd; - char path[PATH_MAX], tmp[PATH_MAX]; + struct stat st; + char path[PATH_MAX]; - tst_resm(TINFO, "creating a file with O_TMPFILE flag"); - fd = SAFE_OPEN(cleanup, ".", O_TMPFILE | O_RDWR, 0600); - - tst_resm(TINFO, "writing data to the file"); - write_file(fd); - - SAFE_FSTAT(cleanup, fd, &st); - tst_resm(TINFO, "file size is '%li'", (long)st.st_size); + tst_res(TINFO, "Testing creation and linking of single temp file"); + fds[0] = SAFE_OPEN(".", O_TMPFILE | O_RDWR, 0600); + write_file(fds[0]); + SAFE_FSTAT(fds[0], &st); if (st.st_size != blocks_num * size) { - tst_resm(TFAIL, "not expected size: '%li' != '%zu'", + tst_res(TFAIL, "Unexpected test file size: %li != %zu", (long)st.st_size, blocks_num * size); - SAFE_CLOSE(cleanup, fd); + } else { + tst_res(TPASS, "Test file size is %li", (long)st.st_size); + } + + if (!tst_dir_is_empty(".", 1)) + tst_res(TFAIL, "Test directory is not empty"); + else + tst_res(TPASS, "Test directory is empty"); + + snprintf(path, PATH_MAX, "/proc/self/fd/%d", fds[0]); + tst_res(TINFO, "Linking unnamed test file to 'tmpfile'"); + SAFE_LINKAT(AT_FDCWD, path, AT_FDCWD, "tmpfile", AT_SYMLINK_FOLLOW); + + if (tst_dir_is_empty(".", 1)) { + tst_res(TFAIL, "Test directory is still empty"); + SAFE_CLOSE(fds[0]); return; } - tst_resm(TINFO, "looking for the file in '.'"); - if (!tst_dir_is_empty(cleanup, ".", 1)) - tst_brkm(TFAIL, cleanup, "found a file, this is not expected"); - tst_resm(TINFO, "file not found, OK"); + if (access("tmpfile", F_OK)) { + tst_res(TFAIL | TERRNO, "Linked test file not found"); + SAFE_CLOSE(fds[0]); + return; + } - snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd); - SAFE_READLINK(cleanup, path, tmp, PATH_MAX); - - tst_resm(TINFO, "renaming '%s' -> 'tmpfile'", tmp); - SAFE_LINKAT(cleanup, AT_FDCWD, path, AT_FDCWD, "tmpfile", - AT_SYMLINK_FOLLOW); - - if (tst_dir_is_empty(cleanup, ".", 1)) - tst_brkm(TFAIL, cleanup, "file not found"); - - SAFE_UNLINK(cleanup, "tmpfile"); - SAFE_CLOSE(cleanup, fd); - - tst_resm(TPASS, "single file tests passed"); + tst_res(TPASS, "Test file was linked correctly"); + SAFE_UNLINK("tmpfile"); + SAFE_CLOSE(fds[0]); } -static void read_file(int fd) +static int read_file(int fd) { int i; char tmp[size]; - SAFE_LSEEK(cleanup, fd, 0, SEEK_SET); + SAFE_LSEEK(fd, 0, SEEK_SET); for (i = 0; i < blocks_num; ++i) { - SAFE_READ(cleanup, 0, fd, tmp, size); - if (memcmp(buf, tmp, size)) - tst_brkm(TFAIL, cleanup, "got unexepected data"); + SAFE_READ(0, fd, tmp, size); + + if (memcmp(buf, tmp, size)) { + tst_res(TFAIL, "got unexepected data"); + return 1; + } } + + return 0; } static void test02(void) { - const int files_num = 100; - int i, fd[files_num]; + int i, fails = 0; char path[PATH_MAX]; - tst_resm(TINFO, "create files in multiple directories"); - for (i = 0; i < files_num; ++i) { + tst_res(TINFO, "Testing temp files in multiple directories"); + for (i = 0; i < FILE_COUNT; ++i) { snprintf(path, PATH_MAX, "tst02_%d", i); - SAFE_MKDIR(cleanup, path, 0700); - SAFE_CHDIR(cleanup, path); - - fd[i] = SAFE_OPEN(cleanup, ".", O_TMPFILE | O_RDWR, 0600); + SAFE_MKDIR(path, 0700); + SAFE_CHDIR(path); + fds[i] = SAFE_OPEN(".", O_TMPFILE | O_RDWR, 0600); } - tst_resm(TINFO, "removing test directories"); - for (i = files_num - 1; i >= 0; --i) { - SAFE_CHDIR(cleanup, ".."); + tst_res(TINFO, "Removing test directories"); + for (i = FILE_COUNT - 1; i >= 0; --i) { + SAFE_CHDIR(".."); snprintf(path, PATH_MAX, "tst02_%d", i); - SAFE_RMDIR(cleanup, path); + SAFE_RMDIR(path); } - tst_resm(TINFO, "writing/reading temporary files"); - for (i = 0; i < files_num; ++i) { - write_file(fd[i]); - read_file(fd[i]); + tst_res(TINFO, "Writing and reading temporary files"); + for (i = 0; i < FILE_COUNT; ++i) { + write_file(fds[i]); + fails += read_file(fds[i]); } - tst_resm(TINFO, "closing temporary files"); - for (i = 0; i < files_num; ++i) - SAFE_CLOSE(cleanup, fd[i]); + tst_res(TINFO, "Closing temporary files"); + for (i = 0; i < FILE_COUNT; ++i) + SAFE_CLOSE(fds[i]); - tst_resm(TPASS, "multiple files tests passed"); + if (!fails) + tst_res(TPASS, "Multiple files test passed"); } static void link_tmp_file(int fd) { char path1[PATH_MAX], path2[PATH_MAX]; - snprintf(path1, PATH_MAX, "/proc/self/fd/%d", fd); - snprintf(path2, PATH_MAX, "tmpfile_%d", fd); - - SAFE_LINKAT(cleanup, AT_FDCWD, path1, AT_FDCWD, path2, - AT_SYMLINK_FOLLOW); + snprintf(path1, PATH_MAX, "/proc/self/fd/%d", fd); + snprintf(path2, PATH_MAX, "tmpfile_%d", fd); + SAFE_LINKAT(AT_FDCWD, path1, AT_FDCWD, path2, AT_SYMLINK_FOLLOW); } static void test03(void) { - const int files_num = 100; const mode_t test_perms[] = { 0, 07777, 001, 0755, 0644, 0440 }; - int i, fd[files_num]; + int i, fails = 0; char path[PATH_MAX]; struct stat st; mode_t mask = umask(0), perm; umask(mask); + tst_res(TINFO, "Testing linked temp files access mode"); - tst_resm(TINFO, "create multiple directories, link files into them"); - tst_resm(TINFO, "and check file permissions"); - for (i = 0; i < files_num; ++i) { - + for (i = 0; i < FILE_COUNT; ++i) { snprintf(path, PATH_MAX, "tst03_%d", i); - SAFE_MKDIR(cleanup, path, 0700); - SAFE_CHDIR(cleanup, path); + SAFE_MKDIR(path, 0700); + SAFE_CHDIR(path); perm = test_perms[i % ARRAY_SIZE(test_perms)]; + fds[i] = SAFE_OPEN(".", O_TMPFILE | O_RDWR, perm); + write_file(fds[i]); + read_file(fds[i]); + link_tmp_file(fds[i]); - fd[i] = SAFE_OPEN(cleanup, ".", O_TMPFILE | O_RDWR, perm); + snprintf(path, PATH_MAX, "tmpfile_%d", fds[i]); + SAFE_LSTAT(path, &st); + perm &= ~mask; - write_file(fd[i]); - read_file(fd[i]); - - link_tmp_file(fd[i]); - - snprintf(path, PATH_MAX, "tmpfile_%d", fd[i]); - - SAFE_LSTAT(cleanup, path, &st); - - mode_t exp_mode = perm & ~mask; - - if ((st.st_mode & ~S_IFMT) != exp_mode) { - tst_brkm(TFAIL, cleanup, - "file mode read %o, but expected %o", - st.st_mode & ~S_IFMT, exp_mode); + if ((st.st_mode & ~S_IFMT) != perm) { + tst_res(TFAIL, "Unexpected access mode: %04o != %04o", + st.st_mode & ~S_IFMT, perm); + fails++; } } - tst_resm(TINFO, "remove files, directories"); - for (i = files_num - 1; i >= 0; --i) { - snprintf(path, PATH_MAX, "tmpfile_%d", fd[i]); - SAFE_UNLINK(cleanup, path); - SAFE_CLOSE(cleanup, fd[i]); + if (!fails) + tst_res(TPASS, "File access modes are correct"); - SAFE_CHDIR(cleanup, ".."); + tst_res(TINFO, "Removing files and directories"); + for (i = FILE_COUNT - 1; i >= 0; --i) { + snprintf(path, PATH_MAX, "tmpfile_%d", fds[i]); + SAFE_UNLINK(path); + SAFE_CLOSE(fds[i]); + + SAFE_CHDIR(".."); snprintf(path, PATH_MAX, "tst03_%d", i); - SAFE_RMDIR(cleanup, path); + SAFE_RMDIR(path); } - - tst_resm(TPASS, "file permission tests passed"); } -int main(int ac, char *av[]) +static void run(void) { - int lc; + test01(); + test02(); + test03(); +} - tst_parse_opts(ac, av, NULL, NULL); +static void cleanup(void) +{ + int i; - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - tst_count = 0; - test01(); - test02(); - test03(); + for (i = 0; i < FILE_COUNT; i++) { + if (fds[i] >= 0) + SAFE_CLOSE(fds[i]); } - cleanup(); - tst_exit(); + SAFE_CHDIR(".."); } + +static struct tst_test test = { + .setup = setup, + .test_all = run, + .cleanup = cleanup, + .needs_root = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1 +}; diff --git a/testcases/kernel/syscalls/open/open15.c b/testcases/kernel/syscalls/open/open15.c new file mode 100644 index 00000000..4917937a --- /dev/null +++ b/testcases/kernel/syscalls/open/open15.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Author: David Fenner, Jon Hendrickson + * Copyright (C) 2024 Andrea Cervesato + */ + +/*\ + * This test verifies that open() is working correctly on symlink() + * generated files. We generate a file via symlink, then we read both from file + * and symlink, comparing that data has been correctly written. + */ + +#include "tst_test.h" + +#define FILENAME "myfile.txt" +#define SYMBNAME "myfile_symlink" +#define BIG_STRING "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + +static char buff_file[128]; +static char buff_symb[128]; +static int str_size; + +static void run(void) +{ + int fd_file, fd_symb; + + memset(buff_file, 0, sizeof(buff_file)); + memset(buff_symb, 0, sizeof(buff_symb)); + + tst_res(TINFO, "Create symlink"); + SAFE_TOUCH(FILENAME, 0777, NULL); + SAFE_SYMLINK(FILENAME, SYMBNAME); + + fd_file = SAFE_OPEN(FILENAME, O_RDONLY, 0777); + fd_symb = SAFE_OPEN(SYMBNAME, O_RDWR, 0777); + + tst_res(TINFO, "Write data via symlink"); + SAFE_WRITE(SAFE_WRITE_ALL, fd_symb, BIG_STRING, str_size); + SAFE_LSEEK(fd_symb, 0, 0); + + tst_res(TINFO, "Read data via file"); + SAFE_READ(1, fd_file, buff_file, str_size); + SAFE_LSEEK(fd_file, 0, 0); + + tst_res(TINFO, "Read data via symlink"); + SAFE_READ(1, fd_symb, buff_symb, str_size); + SAFE_LSEEK(fd_symb, 0, 0); + + TST_EXP_EXPR(!strncmp(buff_file, BIG_STRING, str_size), + "file data has been correctly written"); + + TST_EXP_EXPR(!strncmp(buff_file, buff_symb, str_size), + "file data is the equivalent to symlink generated file data"); + + SAFE_CLOSE(fd_file); + SAFE_CLOSE(fd_symb); + + SAFE_UNLINK(SYMBNAME); + SAFE_UNLINK(FILENAME); +} + +static void setup(void) +{ + str_size = strlen(BIG_STRING); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at01.c b/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at01.c index 6171229f..29a0d83c 100755 --- a/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at01.c +++ b/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic open_by_handle_at() tests. * * [Algorithm] diff --git a/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at02.c b/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at02.c index a505deeb..b445f1ad 100755 --- a/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at02.c +++ b/testcases/kernel/syscalls/open_by_handle_at/open_by_handle_at02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Failure tests for open_by_handle_at(). */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/openat/openat01.c b/testcases/kernel/syscalls/openat/openat01.c index ad400b47..2e63bc2b 100755 --- a/testcases/kernel/syscalls/openat/openat01.c +++ b/testcases/kernel/syscalls/openat/openat01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test case will verify basic function of openat. * * - pathname is relative, then it is interpreted relative to the directory diff --git a/testcases/kernel/syscalls/openat/openat02.c b/testcases/kernel/syscalls/openat/openat02.c index 012b135e..260d344e 100755 --- a/testcases/kernel/syscalls/openat/openat02.c +++ b/testcases/kernel/syscalls/openat/openat02.c @@ -1,65 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2014 Fujitsu Ltd. - * Author: Xing Gu - * - * 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. + * Copyright (c) 2014 Fujitsu Ltd Xing Gu + * Copyright (C) 2025 SUSE LLC Wei Gao */ -/* - * Description: - * Verify that, - * 1)openat() succeeds to open a file in append mode, when - * 'flags' is set to O_APPEND. - * 2)openat() succeeds to enable the close-on-exec flag for a - * file descriptor, when 'flags' is set to O_CLOEXEC. - * 3)openat() succeeds to allow files whose sizes cannot be - * represented in an off_t but can be represented in an off_t - * to be opened, when 'flags' is set to O_LARGEFILE. - * 4)openat() succeeds to not update the file last access time - * (st_atime in the inode) when the file is read, when 'flags' - * is set to O_NOATIME. - * 5)openat() succeeds to open the file failed if the file is a - * symbolic link, when 'flags' is set to O_NOFOLLOW. - * 6)openat() succeeds to truncate the file to length 0 if the file - * already exists and is a regular file and the open mode allows - * writing, when 'flags' is set to O_TRUNC. + +/*\ + * This test case will verify following scenarios of openat. + * + * - openat() succeeds to open a file in append mode, when + * 'flags' is set to O_APPEND. + * + * - openat() succeeds to enable the close-on-exec flag for a + * file descriptor, when 'flags' is set to O_CLOEXEC. + * + * - openat() succeeds to allow files whose sizes cannot be + * represented in an off_t but can be represented in an off_t + * to be opened, when 'flags' is set to O_LARGEFILE. + * + * - openat() succeeds to not update the file last access time + * (st_atime in the inode) when the file is read, when 'flags' + * is set to O_NOATIME. + * + * - openat() succeeds to open the file failed if the file is a + * symbolic link, when 'flags' is set to O_NOFOLLOW. + * + * - openat() succeeds to truncate the file to length 0 if the file + * already exists and is a regular file and the open mode allows + * writing, when 'flags' is set to O_TRUNC. */ #define _GNU_SOURCE - #include #include -#include -#include #include +#include +#include #include -#include -#include - -#include "test.h" -#include "safe_macros.h" -#include "lapi/fcntl.h" -#include "openat.h" +#include "lapi/mount.h" +#include "tst_test.h" #define TEST_APP "openat02_child" - -#define TEST_FILE "test_file" -#define SFILE "sfile" -#define LARGE_FILE "large_file" - +#define MOUNT_POINT "mntpoint" +#define TEST_FILE MOUNT_POINT"/test_file" +#define SFILE MOUNT_POINT"/sfile" +#define LARGE_FILE MOUNT_POINT"/large_file" #define STR "abcdefg" -static void setup(void); -static void cleanup(void); +static int dir_fd, fd; static void testfunc_append(void); static void testfunc_cloexec(void); @@ -68,220 +55,249 @@ static void testfunc_noatime(void); static void testfunc_nofollow(void); static void testfunc_trunc(void); -static void (*testfunc[])(void) = { - testfunc_append, - testfunc_cloexec, - testfunc_largefile, - testfunc_noatime, - testfunc_nofollow, - testfunc_trunc, +static struct test_case { + void (*testfunc)(void); +} test_cases[] = { + { + .testfunc = testfunc_append, + }, + { + .testfunc = testfunc_cloexec, + }, + { + .testfunc = testfunc_largefile, + }, + { + .testfunc = testfunc_noatime, + }, + { + .testfunc = testfunc_nofollow, + }, + { + .testfunc = testfunc_trunc, + }, }; -char *TCID = "openat02"; -int TST_TOTAL = ARRAY_SIZE(testfunc); - -int main(int ac, char **av) -{ - int lc; - int i; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; i < TST_TOTAL; i++) - (*testfunc[i])(); - } - - cleanup(); - tst_exit(); -} - -void setup(void) -{ - TEST_PAUSE; - - tst_sig(FORK, DEF_HANDLER, cleanup); - - tst_tmpdir(); - - SAFE_FILE_PRINTF(cleanup, TEST_FILE, "test file"); - - SAFE_SYMLINK(cleanup, TEST_FILE, SFILE); -} - -void testfunc_append(void) +static void testfunc_append(void) { off_t file_offset; - SAFE_FILE_PRINTF(cleanup, TEST_FILE, "test file"); + TST_EXP_FD(openat(AT_FDCWD, TEST_FILE, O_APPEND | O_RDWR, 0777)); - TEST(openat(AT_FDCWD, TEST_FILE, O_APPEND | O_RDWR, 0777)); + SAFE_WRITE(SAFE_WRITE_ALL, TST_RET, STR, sizeof(STR) - 1); - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "openat failed"); - return; - } - - SAFE_WRITE(cleanup, SAFE_WRITE_ALL, TEST_RETURN, STR, sizeof(STR) - 1); - - file_offset = SAFE_LSEEK(cleanup, TEST_RETURN, 0, SEEK_CUR); + file_offset = SAFE_LSEEK(TST_RET, 0, SEEK_CUR); if (file_offset > (off_t)(sizeof(STR) - 1)) - tst_resm(TPASS, "test O_APPEND for openat success"); + tst_res(TPASS, "test O_APPEND for openat success"); else - tst_resm(TFAIL, "test O_APPEND for openat failed"); + tst_res(TFAIL, "test O_APPEND for openat failed"); - SAFE_CLOSE(cleanup, TEST_RETURN); + SAFE_CLOSE(TST_RET); } -void testfunc_cloexec(void) +static void testfunc_cloexec(void) { pid_t pid; int status; char buf[20]; - TEST(openat(AT_FDCWD, TEST_FILE, O_CLOEXEC | O_RDWR, 0777)); + TST_EXP_FD(openat(AT_FDCWD, TEST_FILE, O_CLOEXEC | O_RDWR, 0777)); - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "openat failed"); - return; - } + sprintf(buf, "%ld", TST_RET); - sprintf(buf, "%ld", TEST_RETURN); - - pid = tst_fork(); + pid = SAFE_FORK(); if (pid < 0) - tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); + tst_brk(TBROK | TERRNO, "fork() failed"); if (pid == 0) { if (execlp(TEST_APP, TEST_APP, buf, NULL)) exit(2); } - SAFE_CLOSE(cleanup, TEST_RETURN); + SAFE_CLOSE(TST_RET); - SAFE_WAIT(cleanup, &status); + SAFE_WAIT(&status); if (WIFEXITED(status)) { switch ((int8_t)WEXITSTATUS(status)) { case 0: - tst_resm(TPASS, "test O_CLOEXEC for openat success"); + tst_res(TPASS, "test O_CLOEXEC for openat success"); break; case 1: - tst_resm(TFAIL, "test O_CLOEXEC for openat failed"); + tst_res(TFAIL, "test O_CLOEXEC for openat failed"); break; default: - tst_brkm(TBROK, cleanup, "execlp() failed"); + tst_brk(TBROK, "execlp() failed"); } } else { - tst_brkm(TBROK, cleanup, - "openat2_exec exits with unexpected error"); + tst_brk(TBROK, "openat2_exec exits with unexpected error"); } } -void testfunc_largefile(void) +static void testfunc_largefile(void) { int fd; off_t offset; - fd = SAFE_OPEN(cleanup, LARGE_FILE, + fd = SAFE_OPEN(LARGE_FILE, O_LARGEFILE | O_RDWR | O_CREAT, 0777); offset = lseek(fd, 4.1*1024*1024*1024, SEEK_SET); if (offset == -1) - tst_brkm(TBROK | TERRNO, cleanup, "lseek64 failed"); + tst_brk(TBROK | TERRNO, "lseek64 failed"); - SAFE_WRITE(cleanup, SAFE_WRITE_ALL, fd, STR, sizeof(STR) - 1); + SAFE_WRITE(SAFE_WRITE_ALL, fd, STR, sizeof(STR) - 1); - SAFE_CLOSE(cleanup, fd); + SAFE_CLOSE(fd); - TEST(openat(AT_FDCWD, LARGE_FILE, O_LARGEFILE | O_RDONLY, 0777)); + TST_EXP_FD(openat(AT_FDCWD, LARGE_FILE, O_LARGEFILE | O_RDONLY, 0777)); - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "test O_LARGEFILE for openat failed"); + if (TST_RET == -1) { + tst_res(TFAIL, "test O_LARGEFILE for openat failed"); } else { - tst_resm(TPASS, "test O_LARGEFILE for openat success"); - SAFE_CLOSE(cleanup, TEST_RETURN); + tst_res(TPASS, "test O_LARGEFILE for openat success"); + SAFE_CLOSE(TST_RET); } } -void testfunc_noatime(void) +static void testfunc_noatime(void) { struct stat file_stat, file_newstat; char buf; const char *flags[] = {"noatime", "relatime", NULL}; int ret; - ret = tst_path_has_mnt_flags(cleanup, NULL, flags); + char path[PATH_MAX]; + char *tmpdir; + + tmpdir = tst_tmpdir_path(); + snprintf(path, sizeof(path), "%s/%s", tmpdir, MOUNT_POINT); + ret = tst_path_has_mnt_flags(path, flags); if (ret > 0) { - tst_resm(TCONF, "test O_NOATIME flag for openat needs " + tst_res(TCONF, "test O_NOATIME flag for openat needs " "filesystems which are mounted without " "noatime and relatime"); return; } - SAFE_STAT(cleanup, TEST_FILE, &file_stat); + SAFE_STAT(TEST_FILE, &file_stat); sleep(1); - TEST(openat(AT_FDCWD, TEST_FILE, O_NOATIME | O_RDONLY, 0777)); + TST_EXP_FD(openat(AT_FDCWD, TEST_FILE, O_NOATIME | O_RDONLY, 0777)); - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "openat failed"); + if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "openat failed"); return; } - SAFE_READ(cleanup, 1, TEST_RETURN, &buf, 1); + SAFE_READ(1, TST_RET, &buf, 1); - SAFE_CLOSE(cleanup, TEST_RETURN); + SAFE_CLOSE(TST_RET); - SAFE_STAT(cleanup, TEST_FILE, &file_newstat); + SAFE_STAT(TEST_FILE, &file_newstat); if (file_stat.st_atime == file_newstat.st_atime) - tst_resm(TPASS, "test O_NOATIME for openat success"); + tst_res(TPASS, "test O_NOATIME for openat success"); else - tst_resm(TFAIL, "test O_NOATIME for openat failed"); + tst_res(TFAIL, "test O_NOATIME for openat failed"); } -void testfunc_nofollow(void) +static void testfunc_nofollow(void) { - TEST(openat(AT_FDCWD, SFILE, O_NOFOLLOW | O_RDONLY, 0777)); - - if (TEST_RETURN == -1 && TEST_ERRNO == ELOOP) { - tst_resm(TPASS, "test O_NOFOLLOW for openat success"); - } else { - tst_resm(TFAIL, "test O_NOFOLLOW for openat failed"); - SAFE_CLOSE(cleanup, TEST_RETURN); - } + TST_EXP_FD_OR_FAIL(openat(AT_FDCWD, SFILE, O_NOFOLLOW | O_RDONLY, 0777), + ELOOP); } -void testfunc_trunc(void) +static void testfunc_trunc(void) { struct stat file_stat; - TEST(openat(AT_FDCWD, TEST_FILE, O_TRUNC | O_RDWR, 0777)); + TST_EXP_FD(openat(AT_FDCWD, TEST_FILE, O_TRUNC | O_RDWR, 0777)); - if (TEST_RETURN == -1) { - tst_resm(TFAIL | TTERRNO, "openat failed"); + if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "openat failed"); return; } - SAFE_FSTAT(cleanup, TEST_RETURN, &file_stat); + SAFE_FSTAT(TST_RET, &file_stat); if (file_stat.st_size == 0) - tst_resm(TPASS, "test O_TRUNC for openat success"); + tst_res(TPASS, "test O_TRUNC for openat success"); else - tst_resm(TFAIL, "test O_TRUNC for openat failed"); + tst_res(TFAIL, "test O_TRUNC for openat failed"); - SAFE_CLOSE(cleanup, TEST_RETURN); + SAFE_CLOSE(TST_RET); } -void cleanup(void) +static void verify_openat(unsigned int n) { - tst_rmdir(); + struct test_case *tc = &test_cases[n]; + + tc->testfunc(); } + +static void setup(void) +{ + SAFE_FILE_PRINTF(TEST_FILE, "test file"); + SAFE_SYMLINK(TEST_FILE, SFILE); +} + +static void cleanup(void) +{ + if (fd > 0) + SAFE_CLOSE(fd); + if (dir_fd > 0) + SAFE_CLOSE(dir_fd); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test = verify_openat, + .tcnt = ARRAY_SIZE(test_cases), + .forks_child = 1, + .all_filesystems = 1, + .needs_root = 1, + .mount_device = 1, + .mntpoint = MOUNT_POINT, + .filesystems = (struct tst_fs[]) { + { + .type = "ext2", + .mnt_flags = MS_STRICTATIME, + }, + { + .type = "ext3", + .mnt_flags = MS_STRICTATIME, + }, + { + .type = "ext4", + .mnt_flags = MS_STRICTATIME, + }, + { + .type = "xfs", + .mnt_flags = MS_STRICTATIME, + }, + { + .type = "btrfs", + .mnt_flags = MS_STRICTATIME, + }, + { + .type = "bcachefs", + .mnt_flags = MS_STRICTATIME, + }, + { + .type = "tmpfs", + .mnt_flags = MS_STRICTATIME, + }, + {} + }, + .skip_filesystems = (const char *[]) { + "vfat", + "exfat", + "ntfs", + NULL + } +}; diff --git a/testcases/kernel/syscalls/openat/openat04.c b/testcases/kernel/syscalls/openat/openat04.c index df5956cd..d3f840ec 100644 --- a/testcases/kernel/syscalls/openat/openat04.c +++ b/testcases/kernel/syscalls/openat/openat04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Check setgid strip logic whether works correctly when creating tmpfile under * filesystem without POSIX ACL supported(by using noacl mount option). Test it * with umask S_IXGRP and also check file mode whether has filtered S_IXGRP. diff --git a/testcases/kernel/syscalls/pathconf/.gitignore b/testcases/kernel/syscalls/pathconf/.gitignore index 31abe8a2..82e38b25 100755 --- a/testcases/kernel/syscalls/pathconf/.gitignore +++ b/testcases/kernel/syscalls/pathconf/.gitignore @@ -1 +1,2 @@ /pathconf01 +/pathconf02 diff --git a/testcases/kernel/syscalls/pathconf/pathconf01.c b/testcases/kernel/syscalls/pathconf/pathconf01.c index 362bae94..5af45e41 100755 --- a/testcases/kernel/syscalls/pathconf/pathconf01.c +++ b/testcases/kernel/syscalls/pathconf/pathconf01.c @@ -1,237 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * 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/ - * + * Copyright (c) Linux Test Project, 2000-2023 + * Authors: William Roske, Dave Fenner */ -/* $Id: pathconf01.c,v 1.5 2009/11/02 13:57:17 subrata_modak Exp $ */ -/********************************************************** - * - * OS Test - Silicon Graphics, Inc. - * - * TEST IDENTIFIER : pathconf01 - * - * EXECUTED BY : anyone - * - * TEST TITLE : Basic test for pathconf(2) - * - * PARENT DOCUMENT : usctpl01 - * - * TEST CASE TOTAL : 6 - * - * WALL CLOCK TIME : 1 - * - * CPU TYPES : ALL - * - * AUTHOR : William Roske - * - * CO-PILOT : Dave Fenner - * - * DATE STARTED : 03/30/92 - * - * INITIAL RELEASE : UNICOS 7.0 - * - * TEST CASES - * - * 1.) pathconf(2) returns...(See Description) - * - * INPUT SPECIFICATIONS - * The standard options for system call tests are accepted. - * (See the parse_opts(3) man page). - * - * OUTPUT SPECIFICATIONS - * - * DURATION - * Terminates - with frequency and infinite modes. - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * RESOURCES - * None - * - * ENVIRONMENTAL NEEDS - * No run-time environmental needs. - * - * SPECIAL PROCEDURAL REQUIREMENTS - * None - * - * INTERCASE DEPENDENCIES - * None - * - * DETAILED DESCRIPTION - * This is a Phase I test for the pathconf(2) system call. It is intended - * to provide a limited exposure of the system call, for now. It - * should/will be extended when full functional tests are written for - * pathconf(2). - * - * Setup: - * Setup signal handling. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, Issue a PASS message. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * - * - *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/ -#include -#include -#include -#include -#include "test.h" +/*\ + * Check the basic functionality of the pathconf(2) system call. + */ -void setup(); -void cleanup(); -void help(); +#include +#include "tst_test.h" -struct pathconf_args { - char *define_tag; +#define NAME_DESC(x) .value = x, .name = #x + +static char *path; + +static struct tcase { int value; -} args[] = { - { - "_PC_LINK_MAX", _PC_LINK_MAX}, { - "_PC_NAME_MAX", _PC_NAME_MAX}, { - "_PC_PATH_MAX", _PC_PATH_MAX}, { - "_PC_PIPE_BUF", _PC_PIPE_BUF}, { - "_PC_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED}, { - "_PC_NO_TRUNC", _PC_NO_TRUNC}, { - NULL, 0} + char *name; +} tcases[] = { + {NAME_DESC(_PC_LINK_MAX)}, + {NAME_DESC(_PC_MAX_CANON)}, + {NAME_DESC(_PC_MAX_INPUT)}, + {NAME_DESC(_PC_NAME_MAX)}, + {NAME_DESC(_PC_PATH_MAX)}, + {NAME_DESC(_PC_PIPE_BUF)}, + {NAME_DESC(_PC_CHOWN_RESTRICTED)}, + {NAME_DESC(_PC_NO_TRUNC)}, + {NAME_DESC(_PC_VDISABLE)}, + {NAME_DESC(_PC_SYNC_IO)}, + {NAME_DESC(_PC_ASYNC_IO)}, + {NAME_DESC(_PC_PRIO_IO)}, + {NAME_DESC(_PC_FILESIZEBITS)}, + {NAME_DESC(_PC_REC_INCR_XFER_SIZE)}, + {NAME_DESC(_PC_REC_MAX_XFER_SIZE)}, + {NAME_DESC(_PC_REC_MIN_XFER_SIZE)}, + {NAME_DESC(_PC_REC_XFER_ALIGN)}, }; -char *TCID = "pathconf01"; -int TST_TOTAL = ARRAY_SIZE(args); +static void verify_pathconf(unsigned int i) +{ + struct tcase *tc = &tcases[i]; -int i; + path = tst_tmpdir_path(); + TEST(pathconf(path, tc->value)); -int lflag; -char *path; + if (TST_RET == -1 && errno != 0) + tst_res(TFAIL, "pathconf Failed, errno = %d", TST_ERR); + else + tst_res(TPASS, "pathconf(%s, %s)", path, tc->name); +} -option_t options[] = { - {"l:", &lflag, &path}, /* -l */ - {NULL, NULL, NULL} +static struct tst_test test = { + .needs_tmpdir = 1, + .test = verify_pathconf, + .tcnt = ARRAY_SIZE(tcases), }; - -int main(int ac, char **av) -{ - int lc; - - tst_parse_opts(ac, av, options, &help); - - if (!lflag) { - tst_tmpdir(); - path = tst_get_tmpdir(); - } - /*************************************************************** - * perform global setup for test - ***************************************************************/ - setup(); - - /*************************************************************** - * check looping state if -c option given - ***************************************************************/ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - for (i = 0; i < TST_TOTAL; i++) { - - errno = -4; - - /* - * Call pathconf(2) - */ - TEST(pathconf(path, args[i].value)); - - /* - * A test case can only fail if -1 is returned and the errno - * was set. If the errno remains unchanged, the - * system call did not fail. - */ - if (TEST_RETURN == -1 && errno != -4) { - tst_resm(TFAIL, - "pathconf(%s, %s) Failed, errno=%d : %s", - path, args[i].define_tag, TEST_ERRNO, - strerror(TEST_ERRNO)); - } else { - tst_resm(TPASS, - "pathconf(%s, %s) returned %ld", - path, args[i].define_tag, - TEST_RETURN); - } - } - } - - /*************************************************************** - * cleanup and exit - ***************************************************************/ - cleanup(); - - tst_exit(); -} - -/*************************************************************** - * setup() - performs all ONE TIME setup for this test. - ***************************************************************/ -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - -/*************************************************************** - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - ***************************************************************/ -void cleanup(void) -{ - if (!lflag) { - tst_rmdir(); - free(path); - } -} - -/*************************************************************** - * help - ***************************************************************/ -void help(void) -{ - printf(" -l path a path to test with pathconf(2) (def: /tmp)\n"); -} diff --git a/testcases/kernel/syscalls/pathconf/pathconf02.c b/testcases/kernel/syscalls/pathconf/pathconf02.c new file mode 100644 index 00000000..8c4bf578 --- /dev/null +++ b/testcases/kernel/syscalls/pathconf/pathconf02.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 FUJITSU LIMITED. All rights reserved. + * Author: Yang Xu + */ + +/*\ + * Verify that, + * + * - pathconf() fails with ENOTDIR if a component used as a directory + * in path is not in fact a directory. + * - pathconf() fails with ENOENT if path is an empty string. + * - pathconf() fails with ENAMETOOLONG if path is too long. + * - pathconf() fails with EINVA if name is invalid. + * - pathconf() fails with EACCES if search permission is denied for + * one of the directories in the path prefix of path. + * - pathconf() fails with ELOOP if too many symbolic links were + * encountered while resolving path. + */ + +#define FILEPATH "testfile/testfile_1" +#define TESTELOOP "test_eloop1" +#define PATH_LEN (PATH_MAX + 2) + +#include +#include +#include "tst_test.h" + +static char *fpath; +static char *emptypath; +static char path[PATH_LEN]; +static char *long_path = path; +static char *abs_path; +static char *testeloop; +static struct passwd *user; + +static struct tcase { + char **path; + int name; + int exp_errno; + char *desc; +} tcases[] = { + {&fpath, 0, ENOTDIR, "path prefix is not a directory"}, + {&emptypath, 0, ENOENT, "path is an empty string"}, + {&long_path, 0, ENAMETOOLONG, "path is too long"}, + {&abs_path, -1, EINVAL, "name is invalid"}, + {&abs_path, 0, EACCES, "without full permissions of the path prefix"}, + {&testeloop, 0, ELOOP, "too many symbolic links"}, +}; + +static void verify_fpathconf(unsigned int i) +{ + struct tcase *tc = &tcases[i]; + + if (tc->exp_errno == EACCES) + SAFE_SETEUID(user->pw_uid); + + TST_EXP_FAIL(pathconf(*tc->path, tc->name), tc->exp_errno, + "pathconf() fail with %s", tc->desc); + + if (tc->exp_errno == EACCES) + SAFE_SETEUID(0); +} + +static void setup(void) +{ + user = SAFE_GETPWNAM("nobody"); + + SAFE_TOUCH("testfile", 0777, NULL); + + abs_path = tst_tmpdir_genpath(FILEPATH); + + SAFE_CHMOD(tst_tmpdir_path(), 0); + + memset(path, 'a', PATH_LEN); + + SAFE_SYMLINK("test_eloop1", "test_eloop2"); + SAFE_SYMLINK("test_eloop2", "test_eloop1"); +} + +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .test = verify_fpathconf, + .setup = setup, + .needs_tmpdir = 1, + .bufs = (struct tst_buffers []) { + {&fpath, .str = FILEPATH}, + {&emptypath, .str = ""}, + {&testeloop, .str = TESTELOOP}, + {}, + }, + .needs_root = 1, +}; diff --git a/testcases/kernel/syscalls/pause/.gitignore b/testcases/kernel/syscalls/pause/.gitignore index 358955a1..49235534 100755 --- a/testcases/kernel/syscalls/pause/.gitignore +++ b/testcases/kernel/syscalls/pause/.gitignore @@ -1,3 +1,2 @@ /pause01 /pause02 -/pause03 diff --git a/testcases/kernel/syscalls/pause/pause01.c b/testcases/kernel/syscalls/pause/pause01.c index c88248da..1c1b0403 100755 --- a/testcases/kernel/syscalls/pause/pause01.c +++ b/testcases/kernel/syscalls/pause/pause01.c @@ -1,56 +1,60 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2016 Linux Test Project + * Copyright (c) 2025 Ricardo B. Marlière */ -#include -#include -#include + +/*\ + * Verify that, pause() returns -1 and sets errno to EINTR after receipt of a + * signal which is caught by the calling process. + */ + #include "tst_test.h" -static void sig_handler(int sig LTP_ATTRIBUTE_UNUSED) +static void sig_handler(int signal LTP_ATTRIBUTE_UNUSED) { } -static void do_child(void) +static void do_child(int signal) { - SAFE_SIGNAL(SIGINT, sig_handler); - + SAFE_SIGNAL(signal, sig_handler); TST_CHECKPOINT_WAKE(0); - - TEST(pause()); - if (TST_RET != -1) - tst_res(TFAIL, "pause() succeeded unexpectedly"); - else if (TST_ERR == EINTR) - tst_res(TPASS, "pause() interrupted with EINTR"); - else - tst_res(TFAIL | TTERRNO, "pause() unexpected errno"); - - TST_CHECKPOINT_WAKE(0); - exit(0); + TST_EXP_FAIL(pause(), EINTR); + tst_res(TPASS, "Process resumed from pause()"); } -static void do_test(void) +static void run(int signal) { - int pid, status; + int pid; pid = SAFE_FORK(); - if (pid == 0) - do_child(); + if (!pid) { + do_child(signal); + exit(0); + } TST_CHECKPOINT_WAIT(0); TST_PROCESS_STATE_WAIT(pid, 'S', 0); - kill(pid, SIGINT); + SAFE_KILL(pid, signal); +} - /* - * TST_CHECKPOINT_WAIT has built-in timeout, if pause() doesn't return, - * this checkpoint call will reliably end the test. - */ - TST_CHECKPOINT_WAIT(0); - SAFE_WAIT(&status); +static void run_all(void) +{ + run(SIGHUP); + run(SIGINT); + run(SIGQUIT); + run(SIGILL); + run(SIGTRAP); + run(SIGABRT); + run(SIGFPE); + run(SIGSEGV); + run(SIGPIPE); + run(SIGALRM); + run(SIGTERM); } static struct tst_test test = { .forks_child = 1, .needs_checkpoints = 1, - .test_all = do_test, + .test_all = run_all, }; diff --git a/testcases/kernel/syscalls/pause/pause02.c b/testcases/kernel/syscalls/pause/pause02.c index 32b41bd7..61220993 100755 --- a/testcases/kernel/syscalls/pause/pause02.c +++ b/testcases/kernel/syscalls/pause/pause02.c @@ -1,156 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * Copyright (c) 2012 Cyril Hrubis - * - * 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 + * 07/2001 Ported by Wayne Boyer + * Copyright (c) 2025 Ricardo B. Marlière */ -/* - * Verify that, pause() returns -1 and sets errno to EINTR after receipt of a - * signal which is caught by the calling process. Also, verify that the calling - * process will resume execution from the point of suspension. +/*\ + * Verifies that pause() does not return after proccess receives a SIGKILL signal. */ -#include -#include -#include -#include +#include "tst_test.h" -#include "test.h" - -char *TCID = "pause02"; -int TST_TOTAL = 1; -static pid_t cpid; - -static void do_child(void); -static void setup(void); -static void cleanup(void); - -int main(int ac, char **av) +void do_child(void) +{ + TST_CHECKPOINT_WAKE(0); + pause(); + tst_res(TFAIL, "pause() returned after SIGKILL"); +} + +void run(void) { - int lc; int status; + pid_t pid; - tst_parse_opts(ac, av, NULL, NULL); - -#ifdef UCLINUX - maybe_run_child(&do_child, ""); -#endif - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - cpid = FORK_OR_VFORK(); - switch (cpid) { - case -1: - tst_brkm(TBROK, cleanup, "fork() failed"); - break; - case 0: -#ifdef UCLINUX - if (self_exec(av[0], "") < 0) - tst_brkm(TBROK, cleanup, "self_exec failed"); -#else - do_child(); -#endif - break; - default: - break; - } - - /* - * Wait for child to enter pause(). - */ - TST_PROCESS_STATE_WAIT(cleanup, cpid, 'S'); - - /* - * Send the SIGINT signal now, so that child - * returns from pause and resumes execution. - */ - kill(cpid, SIGINT); - - wait(&status); - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) == 0) - tst_resm(TPASS, "pause was interrupted correctly"); - else - tst_resm(TFAIL, "pause was interrupted but the " - "retval and/or errno was wrong"); - continue; - } - - if (WIFSIGNALED(status)) { - switch (WTERMSIG(status)) { - case SIGALRM: - tst_resm(TFAIL, "Timeout: SIGINT wasn't received by child"); - break; - default: - tst_resm(TFAIL, "Child killed by signal"); - } - - continue; - } - - tst_resm(TFAIL, "Pause was not interrupted"); + pid = SAFE_FORK(); + if (!pid) { + do_child(); + exit(0); } - cleanup(); - tst_exit(); + TST_CHECKPOINT_WAIT(0); + TST_PROCESS_STATE_WAIT(pid, 'S', 10000); + SAFE_KILL(pid, SIGKILL); + SAFE_WAITPID(pid, &status, 0); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + tst_res(TPASS, "pause() killed by SIGKILL"); + else + tst_res(TFAIL, "Child %s", tst_strstatus(status)); } -static void sig_handle(int sig) -{ -} - -static void do_child(void) -{ - /* avoid LTP framework to do whatever it likes */ - signal(SIGALRM, SIG_DFL); - - if (signal(SIGINT, sig_handle) == SIG_ERR) { - fprintf(stderr, "Child: Failed to setup signal handler\n"); - exit(1); - } - - /* Commit suicide after 10 seconds */ - alarm(10); - - TEST(pause()); - - if (TEST_RETURN == -1) { - if (TEST_ERRNO == EINTR) - exit(0); - - fprintf(stderr, "Child: Pause returned -1 but errno is %d (%s)\n", - TEST_ERRNO, strerror(TEST_ERRNO)); - exit(1); - } - - fprintf(stderr, "Child: Pause returned %ld\n", TEST_RETURN); - exit(1); -} - -static void setup(void) -{ - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - -static void cleanup(void) -{ -} +static struct tst_test test = { + .test_all = run, + .needs_checkpoints = 1, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/pause/pause03.c b/testcases/kernel/syscalls/pause/pause03.c deleted file mode 100755 index b1503fc5..00000000 --- a/testcases/kernel/syscalls/pause/pause03.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2001 - * 07/2001 Ported by Wayne Boyer - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -/* - * Test Description: - * pause() does not return due to receipt of SIGKILL signal and specified - * process should be terminated. - */ -#include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" - -static pid_t cpid; - -char *TCID = "pause03"; -int TST_TOTAL = 1; - -static void do_child(void); -static void setup(void); -static void cleanup(void); - -int main(int ac, char **av) -{ - int lc; - int status; - - tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, ""); -#endif - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - if ((cpid = FORK_OR_VFORK()) == -1) - tst_brkm(TBROK | TERRNO, NULL, "fork() failed"); - - if (cpid == 0) { -#ifdef UCLINUX - if (self_exec(av[0], "") < 0) - tst_brkm(TBROK, cleanup, "self_exec failed"); -#else - do_child(); -#endif - } - - TST_PROCESS_STATE_WAIT(cleanup, cpid, 'S'); - - kill(cpid, SIGKILL); - - SAFE_WAIT(NULL, &status); - - if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) { - tst_resm(TPASS, "pause() did not return after SIGKILL"); - continue; - } - - if (WIFSIGNALED(status)) { - tst_resm(TFAIL, "child killed by %s unexpectedly", - tst_strsig(WTERMSIG(status))); - continue; - } - - tst_resm(TFAIL, "child exited with %i", WEXITSTATUS(status)); - } - - cleanup(); - tst_exit(); - -} - -void do_child(void) -{ - TEST(pause()); - - tst_resm(TFAIL, "Unexpected return from pause()"); - - exit(0); -} - -void setup(void) -{ - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - - -void cleanup(void) -{ - kill(cpid, SIGKILL); -} diff --git a/testcases/kernel/syscalls/perf_event_open/perf_event_open01.c b/testcases/kernel/syscalls/perf_event_open/perf_event_open01.c index 30c0d759..86a134b5 100755 --- a/testcases/kernel/syscalls/perf_event_open/perf_event_open01.c +++ b/testcases/kernel/syscalls/perf_event_open/perf_event_open01.c @@ -148,7 +148,7 @@ static void verify(struct test_case_t *tc) TEST(perf_event_open(&pe, 0, -1, -1, 0)); if (TEST_RETURN == -1) { if (TEST_ERRNO == ENOENT || TEST_ERRNO == EOPNOTSUPP || - TEST_ERRNO == ENODEV) { + TEST_ERRNO == ENODEV || TEST_ERRNO == EINVAL) { tst_resm(TCONF | TTERRNO, "perf_event_open for %s not supported", tc->config_name); diff --git a/testcases/kernel/syscalls/perf_event_open/perf_event_open02.c b/testcases/kernel/syscalls/perf_event_open/perf_event_open02.c index defe13c6..7306ecf5 100755 --- a/testcases/kernel/syscalls/perf_event_open/perf_event_open02.c +++ b/testcases/kernel/syscalls/perf_event_open/perf_event_open02.c @@ -334,5 +334,5 @@ static struct tst_test test = { }, .test_all = verify, .needs_root = 1, - .max_runtime = 72 + .timeout = 72 }; diff --git a/testcases/kernel/syscalls/perf_event_open/perf_event_open03.c b/testcases/kernel/syscalls/perf_event_open/perf_event_open03.c index 7dd31d3d..389cc351 100755 --- a/testcases/kernel/syscalls/perf_event_open/perf_event_open03.c +++ b/testcases/kernel/syscalls/perf_event_open/perf_event_open03.c @@ -77,22 +77,41 @@ static void check_progress(int i) static void run(void) { - long diff; - int i; + long diff, diff_total, mem_avail, mem_avail_prev; + int i, sample; - diff = SAFE_READ_MEMINFO("MemAvailable:"); + sample = 0; + diff_total = 0; + + mem_avail_prev = SAFE_READ_MEMINFO("MemAvailable:"); tst_timer_start(CLOCK_MONOTONIC); /* leak about 100MB of RAM */ for (i = 0; i < iterations; i++) { ioctl(fd, PERF_EVENT_IOC_SET_FILTER, "filter,0/0@abcd"); check_progress(i); + + /* + * Every 1200000 iterations, calculate the difference in memory + * availability. If the difference is greater than 20 * 1024 (20MB), + * increment the sample counter and log the event. + */ + if ((i % 1200000) == 0) { + mem_avail = SAFE_READ_MEMINFO("MemAvailable:"); + diff = mem_avail_prev - mem_avail; + diff_total += diff; + + if (diff > 20 * 1024) { + sample++; + tst_res(TINFO, "MemAvailable decreased by %ld kB at iteration %d", diff, i); + } + + mem_avail_prev = mem_avail; + } } - diff -= SAFE_READ_MEMINFO("MemAvailable:"); - - if (diff > 50 * 1024) - tst_res(TFAIL, "Likely kernel memory leak detected"); + if ((sample > 5) || (diff_total > 100 * 1024)) + tst_res(TFAIL, "Likely kernel memory leak detected, total decrease: %ld kB", diff_total); else tst_res(TPASS, "No memory leak found"); } @@ -108,7 +127,7 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .needs_root = 1, - .max_runtime = 300, + .runtime = 300, .tags = (const struct tst_tag[]) { {"linux-git", "7bdb157cdebb"}, {"CVE", "2020-25704"}, diff --git a/testcases/kernel/syscalls/personality/personality01.c b/testcases/kernel/syscalls/personality/personality01.c index 47fb6625..05a0e17f 100755 --- a/testcases/kernel/syscalls/personality/personality01.c +++ b/testcases/kernel/syscalls/personality/personality01.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Tries to set different personalities. * * We set the personality in a child process since it's not guaranteed that we diff --git a/testcases/kernel/syscalls/personality/personality02.c b/testcases/kernel/syscalls/personality/personality02.c index e080284f..7c718dba 100755 --- a/testcases/kernel/syscalls/personality/personality02.c +++ b/testcases/kernel/syscalls/personality/personality02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test checks if select() timeout is not updated when personality with * STICKY_TIMEOUTS is used. */ diff --git a/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd01.c b/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd01.c index 60cf6702..57e96405 100644 --- a/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd01.c +++ b/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic pidfd_getfd() test: * * - the close-on-exec flag is set on the file descriptor returned by diff --git a/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd02.c b/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd02.c index 3431a093..a1ede7a9 100644 --- a/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd02.c +++ b/testcases/kernel/syscalls/pidfd_getfd/pidfd_getfd02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the pidfd_open syscall. * * - EBADF pidfd is not a valid PID file descriptor diff --git a/testcases/kernel/syscalls/pidfd_open/pidfd_open01.c b/testcases/kernel/syscalls/pidfd_open/pidfd_open01.c index ce07e671..a07259d1 100755 --- a/testcases/kernel/syscalls/pidfd_open/pidfd_open01.c +++ b/testcases/kernel/syscalls/pidfd_open/pidfd_open01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic pidfd_open() test: * * - Fetch the PID of the current process and try to get its file descriptor. diff --git a/testcases/kernel/syscalls/pidfd_open/pidfd_open02.c b/testcases/kernel/syscalls/pidfd_open/pidfd_open02.c index 9d6c9321..9c382260 100755 --- a/testcases/kernel/syscalls/pidfd_open/pidfd_open02.c +++ b/testcases/kernel/syscalls/pidfd_open/pidfd_open02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the pidfd_open syscall. * * - ESRCH the process specified by pid does not exist diff --git a/testcases/kernel/syscalls/pidfd_open/pidfd_open03.c b/testcases/kernel/syscalls/pidfd_open/pidfd_open03.c index 16a8442f..2a09de00 100755 --- a/testcases/kernel/syscalls/pidfd_open/pidfd_open03.c +++ b/testcases/kernel/syscalls/pidfd_open/pidfd_open03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This program opens the PID file descriptor of the child process created with * fork(). It then uses poll to monitor the file descriptor for process exit, as * indicated by an EPOLLIN event. diff --git a/testcases/kernel/syscalls/pidfd_open/pidfd_open04.c b/testcases/kernel/syscalls/pidfd_open/pidfd_open04.c index 0e8ab695..7e106ab5 100644 --- a/testcases/kernel/syscalls/pidfd_open/pidfd_open04.c +++ b/testcases/kernel/syscalls/pidfd_open/pidfd_open04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that the PIDFD_NONBLOCK flag works with pidfd_open() and * that waitid() with a non-blocking pidfd returns EAGAIN. */ diff --git a/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal01.c b/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal01.c index 47158502..ab2990db 100755 --- a/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal01.c +++ b/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests if the pidfd_send_signal syscall behaves * like rt_sigqueueinfo when a pointer to a siginfo_t * struct is passed. diff --git a/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal02.c b/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal02.c index a3bf994f..31e8a112 100755 --- a/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal02.c +++ b/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the pidfd_send_signal * system call. * diff --git a/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal03.c b/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal03.c index 20d96b11..e1b13727 100755 --- a/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal03.c +++ b/testcases/kernel/syscalls/pidfd_send_signal/pidfd_send_signal03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This test checks if the pidfd_send_signal syscall wrongfully sends * a signal to a new process which inherited the PID of the actual * target process. diff --git a/testcases/kernel/syscalls/pipe/.gitignore b/testcases/kernel/syscalls/pipe/.gitignore index 774d7320..581a68b7 100755 --- a/testcases/kernel/syscalls/pipe/.gitignore +++ b/testcases/kernel/syscalls/pipe/.gitignore @@ -12,3 +12,4 @@ /pipe12 /pipe13 /pipe14 +/pipe15 diff --git a/testcases/kernel/syscalls/pipe/pipe02.c b/testcases/kernel/syscalls/pipe/pipe02.c index 2247037b..d6cbff92 100755 --- a/testcases/kernel/syscalls/pipe/pipe02.c +++ b/testcases/kernel/syscalls/pipe/pipe02.c @@ -41,10 +41,6 @@ static void verify_pipe(void) memset(wrbuf, 'a', SIZE); -#ifdef UCLINUX - maybe_run_child(&do_child, "dd", &fd[0], &fd[1]); -#endif - TEST(pipe(fd)); if (TST_RET == -1) { tst_res(TFAIL|TTERRNO, "pipe() failed"); @@ -53,12 +49,7 @@ static void verify_pipe(void) pid = SAFE_FORK(); if (pid == 0) { -#ifdef UCLINUX - if (self_exec(av[0], "dd", fd[0], fd[1]) < 0) - tst_brk(TBROK, "self_exec failed"); -#else do_child(); -#endif } memset(rdbuf, 0, SIZE); diff --git a/testcases/kernel/syscalls/pipe/pipe03.c b/testcases/kernel/syscalls/pipe/pipe03.c index d20867b2..50f49b8e 100755 --- a/testcases/kernel/syscalls/pipe/pipe03.c +++ b/testcases/kernel/syscalls/pipe/pipe03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that, an attempt to write to the read end of a pipe fails with EBADF * and an attempt to read from the write end of a pipe also fails with EBADF. */ diff --git a/testcases/kernel/syscalls/pipe/pipe04.c b/testcases/kernel/syscalls/pipe/pipe04.c index a3c56e33..219daecd 100755 --- a/testcases/kernel/syscalls/pipe/pipe04.c +++ b/testcases/kernel/syscalls/pipe/pipe04.c @@ -84,11 +84,6 @@ int main(int ac, char **av) char rbuf[BUFSIZ]; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&c1func, "ndd", 1, &fildes[0], &fildes[1]); - maybe_run_child(&c2func, "ndd", 2, &fildes[0], &fildes[1]); -#endif - setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { @@ -98,30 +93,16 @@ int main(int ac, char **av) SAFE_PIPE(cleanup, fildes); - if ((c1pid = FORK_OR_VFORK()) == -1) + if ((c1pid = tst_fork()) == -1) tst_brkm(TBROK, cleanup, "fork() failed - " "errno %d", errno); if (c1pid == 0) -#ifdef UCLINUX - if (self_exec(av[0], "ndd", 1, fildes[0], fildes[1]) < - 0) { - tst_brkm(TBROK, cleanup, "self_exec failed"); - } -#else c1func(); -#endif - if ((c2pid = FORK_OR_VFORK()) == -1) + if ((c2pid = tst_fork()) == -1) tst_brkm(TBROK, cleanup, "fork() failed - " "errno %d", errno); if (c2pid == 0) -#ifdef UCLINUX - if (self_exec(av[0], "ndd", 2, fildes[0], fildes[1]) < - 0) { - tst_brkm(TBROK, cleanup, "self_exec failed"); - } -#else c2func(); -#endif /* PARENT */ if (close(fildes[1]) == -1) diff --git a/testcases/kernel/syscalls/pipe/pipe06.c b/testcases/kernel/syscalls/pipe/pipe06.c index 0c6bc03b..29dbbdbd 100755 --- a/testcases/kernel/syscalls/pipe/pipe06.c +++ b/testcases/kernel/syscalls/pipe/pipe06.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that, pipe(2) syscall fails with errno EMFILE when * limit on the number of open file descriptors has been reached. */ diff --git a/testcases/kernel/syscalls/pipe/pipe07.c b/testcases/kernel/syscalls/pipe/pipe07.c index 19648568..d1790202 100755 --- a/testcases/kernel/syscalls/pipe/pipe07.c +++ b/testcases/kernel/syscalls/pipe/pipe07.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that, pipe(2) syscall can open the maximum number of * file descriptors permitted. */ @@ -45,6 +43,8 @@ static int record_open_fds(void) opened_fds[num_opened_fds++] = fd; } + SAFE_CLOSEDIR(dir); + return num_opened_fds; } @@ -56,8 +56,8 @@ static void setup(void) tst_res(TINFO, "getdtablesize() = %d", max_fds); pipe_fds = SAFE_MALLOC(max_fds * sizeof(int)); - exp_num_pipes = (max_fds - record_open_fds()) / 2; - tst_res(TINFO, "expected max fds to be opened by pipe(): %d", exp_num_pipes * 2); + exp_num_pipes = (max_fds - record_open_fds()) / 2 * 2; + tst_res(TINFO, "expected max fds to be opened by pipe(): %d", exp_num_pipes); } static void run(void) @@ -73,7 +73,7 @@ static void run(void) } while (!TST_RET); TST_EXP_EQ_LI(errno, EMFILE); - TST_EXP_EQ_LI(exp_num_pipes * 2, num_pipe_fds); + TST_EXP_EQ_LI(exp_num_pipes, num_pipe_fds); for (int i = 0; i < num_pipe_fds; i++) SAFE_CLOSE(pipe_fds[i]); diff --git a/testcases/kernel/syscalls/pipe/pipe08.c b/testcases/kernel/syscalls/pipe/pipe08.c index 28088cf6..f86774d5 100755 --- a/testcases/kernel/syscalls/pipe/pipe08.c +++ b/testcases/kernel/syscalls/pipe/pipe08.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, on any attempt to write to a pipe which is closed for * reading will generate a SIGPIPE signal and write will fail with * EPIPE errno. diff --git a/testcases/kernel/syscalls/pipe/pipe09.c b/testcases/kernel/syscalls/pipe/pipe09.c index a4b2f823..86282de4 100755 --- a/testcases/kernel/syscalls/pipe/pipe09.c +++ b/testcases/kernel/syscalls/pipe/pipe09.c @@ -98,7 +98,7 @@ int main(int ac, char **av) continue; } - if ((fork_1 = FORK_OR_VFORK()) == -1) { + if ((fork_1 = tst_fork()) == -1) { tst_brkm(TBROK, cleanup, "fork() #1 failed"); } @@ -125,7 +125,7 @@ int main(int ac, char **av) tst_brkm(TBROK, cleanup, "child exited abnormally"); } - if ((fork_2 = FORK_OR_VFORK()) == -1) { + if ((fork_2 = tst_fork()) == -1) { tst_brkm(TBROK, cleanup, "fork() #2 failed"); } diff --git a/testcases/kernel/syscalls/pipe/pipe10.c b/testcases/kernel/syscalls/pipe/pipe10.c index 018e653d..30cd983b 100755 --- a/testcases/kernel/syscalls/pipe/pipe10.c +++ b/testcases/kernel/syscalls/pipe/pipe10.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, when a parent process opens a pipe, a child process can * read from it. */ diff --git a/testcases/kernel/syscalls/pipe/pipe14.c b/testcases/kernel/syscalls/pipe/pipe14.c index 2d2969d8..66d94cff 100644 --- a/testcases/kernel/syscalls/pipe/pipe14.c +++ b/testcases/kernel/syscalls/pipe/pipe14.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Verify that, if the write end of a pipe is closed, then a process reading * from the pipe will see end-of-file (i.e., read() returns 0) once it has * read all remaining data in the pipe. diff --git a/testcases/kernel/syscalls/pipe/pipe15.c b/testcases/kernel/syscalls/pipe/pipe15.c new file mode 100644 index 00000000..94b0cb10 --- /dev/null +++ b/testcases/kernel/syscalls/pipe/pipe15.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 SUSE LLC Marius Kittler + */ + +/*\ + * This is a regression test for hangup on pipe operations. See + * https://www.spinics.net/lists/linux-api/msg49762.html for + * additional context. It tests that pipe operations do not block + * indefinitely when going to the soft limit on the total size of + * all pipes created by a single user. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "tst_test.h" +#include "tst_safe_stdio.h" +#include "tst_safe_macros.h" + +static int pipe_count; +static int *pipes; +static char *buffer; + +static void run(void) +{ + const int *const pipe = pipes + 2 * (pipe_count - 1); + const int buffer_size = SAFE_FCNTL(pipe[1], F_GETPIPE_SZ); + + tst_res(TINFO, "Soft-limited buffer size: %d bytes", buffer_size); + SAFE_WRITE(1, pipe[1], buffer, buffer_size); + SAFE_READ(1, pipe[0], buffer, buffer_size - 1); + SAFE_WRITE(1, pipe[1], buffer, 1); + tst_res(TPASS, "Pipe operation did not block"); + + SAFE_READ(1, pipe[0], buffer, 2); +} + +static void setup(void) +{ + int pipe[2]; + int page_size = getpagesize(), soft_limit; + struct rlimit nfd; + + SAFE_PIPE(pipe); + const int buffer_size = SAFE_FCNTL(pipe[1], F_GETPIPE_SZ); + + SAFE_CLOSE(pipe[0]); + SAFE_CLOSE(pipe[1]); + + SAFE_FILE_SCANF("/proc/sys/fs/pipe-user-pages-soft", "%i", &soft_limit); + pipe_count = soft_limit * page_size / buffer_size; + + tst_res(TINFO, "Soft limit for pipes: %i pages", soft_limit); + tst_res(TINFO, "Buffer size: %d byte", buffer_size); + tst_res(TINFO, "Creating %i pipes", pipe_count); + + SAFE_GETRLIMIT(RLIMIT_NOFILE, &nfd); + if (nfd.rlim_max < (unsigned long)pipe_count * 2 + 3) + tst_brk(TCONF, "NOFILE limit max too low: %lu < %i", nfd.rlim_max, pipe_count); + + if (nfd.rlim_cur < nfd.rlim_max) { + nfd.rlim_cur = nfd.rlim_max; + SAFE_SETRLIMIT(RLIMIT_NOFILE, &nfd); + } + + buffer = SAFE_MALLOC(buffer_size); + pipes = SAFE_MALLOC(pipe_count * 2 * sizeof(int)); + for (int i = 0; i < pipe_count; ++i) + SAFE_PIPE(pipes + i * 2); + +} + +static void cleanup(void) +{ + if (pipes) { + for (int i = 0; i < pipe_count * 2; i++) { + if (pipes[i] > 0) + SAFE_CLOSE(pipes[i]); + } + free(pipes); + } + + if (buffer) + free(buffer); +} + +static struct tst_test test = { + .setup = setup, + .test_all = run, + .cleanup = cleanup, + .tags = (const struct tst_tag[]){ + {"linux-git", "46c4c9d1beb7"}, + }, +}; diff --git a/testcases/kernel/syscalls/pkeys/Makefile b/testcases/kernel/syscalls/pkeys/Makefile index 9ee2c2ea..814593f3 100755 --- a/testcases/kernel/syscalls/pkeys/Makefile +++ b/testcases/kernel/syscalls/pkeys/Makefile @@ -5,4 +5,6 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk +pkey01: CFLAGS += -falign-functions=64 + include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/pkeys/pkey01.c b/testcases/kernel/syscalls/pkeys/pkey01.c index 0159822e..8cbe2d6b 100755 --- a/testcases/kernel/syscalls/pkeys/pkey01.c +++ b/testcases/kernel/syscalls/pkeys/pkey01.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2019 Red Hat, Inc. - * + * Copyright (c) 2019-2024 Red Hat, Inc. + */ + +/*\ * Memory Protection Keys for Userspace (PKU aka PKEYs) is a Skylake-SP * server feature that provides a mechanism for enforcing page-based * protections, but without requiring modification of the page tables @@ -10,14 +12,15 @@ * giving 16 possible keys. * * Basic method for PKEYs testing: - * 1. test allocates a pkey(e.g. PKEY_DISABLE_ACCESS) via pkey_alloc() - * 2. pkey_mprotect() apply this pkey to a piece of memory(buffer) - * 3. check if access right of the buffer has been changed and take effect - * 4. remove the access right(pkey) from this buffer via pkey_mprotect() - * 5. check if buffer area can be read or write after removing pkey - * 6. pkey_free() releases the pkey after using it * - * Looping around this basic test on diffenrent types of memory. + * 1. test allocates a pkey(e.g. PKEY_DISABLE_ACCESS) via pkey_alloc() + * 2. pkey_mprotect() apply this pkey to a piece of memory(buffer) + * 3. check if access right of the buffer has been changed and take effect + * 4. remove the access right(pkey) from this buffer via pkey_mprotect() + * 5. check if buffer area can be read or write after removing pkey + * 6. pkey_free() releases the pkey after using it + * + * Looping around this basic test on different types of memory. */ #define _GNU_SOURCE @@ -29,29 +32,43 @@ #include #include -#include "pkey.h" +#include "lapi/pkey.h" #define TEST_FILE "pkey_testfile" #define STR "abcdefghijklmnopqrstuvwxyz12345\n" #define PATH_VM_NRHPS "/proc/sys/vm/nr_hugepages" static int size; +static int execute_supported = 1; +#define PERM_NAME(x) .access_rights = x, .name = #x static struct tcase { unsigned long flags; unsigned long access_rights; char *name; } tcases[] = { - {0, PKEY_DISABLE_ACCESS, "PKEY_DISABLE_ACCESS"}, - {0, PKEY_DISABLE_WRITE, "PKEY_DISABLE_WRITE"}, + {PERM_NAME(PKEY_DISABLE_ACCESS)}, + {PERM_NAME(PKEY_DISABLE_WRITE)}, + {PERM_NAME(PKEY_DISABLE_EXECUTE)} /* keep it the last */ }; static void setup(void) { - int i, fd; + int i, fd, pkey; check_pkey_support(); + pkey = pkey_alloc(0, PKEY_DISABLE_EXECUTE); + if (pkey == -1) { + if (errno == EINVAL) { + tst_res(TINFO, "PKEY_DISABLE_EXECUTE not implemented"); + execute_supported = 0; + } else { + tst_brk(TBROK | TERRNO, "pkey_alloc failed"); + } + } + pkey_free(pkey); + if (tst_hugepages == test.hugepages.number) size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024; else @@ -125,16 +142,35 @@ static char *flag_to_str(int flags) } } -static void pkey_test(struct tcase *tc, struct mmap_param *mpa) +static long __attribute__ ((noinline)) dummy_func(void) +{ + return 0xdead; +} + +/* + * return: 1 if it's safe to quit testing on failure (all following would be + * TCONF, O otherwise. + */ +static int pkey_test(struct tcase *tc, struct mmap_param *mpa) { pid_t pid; char *buffer; int pkey, status; int fd = mpa->fd; + long (*func)(void) = 0; + uintptr_t page_mask = ~(getpagesize() - 1); + uintptr_t offset_mask = (getpagesize() - 1); + uintptr_t func_page_offset = (uintptr_t)&dummy_func & offset_mask; + void *page_to_copy = (void *)((uintptr_t)&dummy_func & page_mask); + + if (!execute_supported && (tc->access_rights == PKEY_DISABLE_EXECUTE)) { + tst_res(TCONF, "skip PKEY_DISABLE_EXECUTE test"); + return 1; + } if (!tst_hugepages && (mpa->flags & MAP_HUGETLB)) { - tst_res(TINFO, "Skip test on (%s) buffer", flag_to_str(mpa->flags)); - return; + tst_res(TCONF, "Skip test on (%s) buffer", flag_to_str(mpa->flags)); + return 0; } if (fd == 0) @@ -142,12 +178,17 @@ static void pkey_test(struct tcase *tc, struct mmap_param *mpa) buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0); - pkey = ltp_pkey_alloc(tc->flags, tc->access_rights); + if (mpa->prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) { + memcpy(buffer, page_to_copy, getpagesize()); + func = (long (*)(void))(buffer + func_page_offset); + } + + pkey = pkey_alloc(tc->flags, tc->access_rights); if (pkey == -1) tst_brk(TBROK | TERRNO, "pkey_alloc failed"); tst_res(TINFO, "Set %s on (%s) buffer", tc->name, flag_to_str(mpa->flags)); - if (ltp_pkey_mprotect(buffer, size, mpa->prot, pkey) == -1) + if (pkey_mprotect(buffer, size, mpa->prot, pkey) == -1) tst_brk(TBROK | TERRNO, "pkey_mprotect failed"); pid = SAFE_FORK(); @@ -164,6 +205,9 @@ static void pkey_test(struct tcase *tc, struct mmap_param *mpa) tst_res(TFAIL | TERRNO, "Write buffer success, buffer[0] = %d", *buffer); break; + case PKEY_DISABLE_EXECUTE: + tst_res(TFAIL | TERRNO, "Execute buffer result = %ld", func()); + break; } exit(0); } @@ -176,7 +220,7 @@ static void pkey_test(struct tcase *tc, struct mmap_param *mpa) tst_res(TFAIL, "Child: %s", tst_strstatus(status)); tst_res(TINFO, "Remove %s from the buffer", tc->name); - if (ltp_pkey_mprotect(buffer, size, mpa->prot, 0x0) == -1) + if (pkey_mprotect(buffer, size, mpa->prot, 0x0) == -1) tst_brk(TBROK | TERRNO, "pkey_mprotect failed"); switch (mpa->prot) { @@ -188,10 +232,15 @@ static void pkey_test(struct tcase *tc, struct mmap_param *mpa) tst_res(TPASS, "Write buffer success, buffer[0] = %d", *buffer); break; case PROT_READ | PROT_WRITE: - case PROT_READ | PROT_WRITE | PROT_EXEC: *buffer = 'a'; tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer); break; + case PROT_READ | PROT_WRITE | PROT_EXEC: + if (dummy_func() == func()) + tst_res(TPASS, "Execute buffer success, result = %ld", dummy_func()); + else + tst_res(TFAIL, "Execute buffer with unexpected result: %ld", func()); + break; } if (fd >= 0) @@ -199,8 +248,10 @@ static void pkey_test(struct tcase *tc, struct mmap_param *mpa) SAFE_MUNMAP(buffer, size); - if (ltp_pkey_free(pkey) == -1) + if (pkey_free(pkey) == -1) tst_brk(TBROK | TERRNO, "pkey_free failed"); + + return 0; } static void verify_pkey(unsigned int i) @@ -213,7 +264,8 @@ static void verify_pkey(unsigned int i) for (j = 0; j < ARRAY_SIZE(mmap_params); j++) { mpa = &mmap_params[j]; - pkey_test(tc, mpa); + if (pkey_test(tc, mpa)) + break; } } diff --git a/testcases/kernel/syscalls/prctl/.gitignore b/testcases/kernel/syscalls/prctl/.gitignore index 50ee4bf6..8bcc22f9 100755 --- a/testcases/kernel/syscalls/prctl/.gitignore +++ b/testcases/kernel/syscalls/prctl/.gitignore @@ -1,7 +1,6 @@ /prctl01 /prctl02 /prctl03 -/prctl04 /prctl05 /prctl06 /prctl06_execve diff --git a/testcases/kernel/syscalls/prctl/prctl01.c b/testcases/kernel/syscalls/prctl/prctl01.c index 939ca38c..ca3f39a6 100755 --- a/testcases/kernel/syscalls/prctl/prctl01.c +++ b/testcases/kernel/syscalls/prctl/prctl01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Basic test for PR_SET_PDEATHSIG/PR_GET_PDEATHSIG * * Use PR_SET_PDEATHSIG to set SIGUSR2 signal and PR_GET_PDEATHSIG should diff --git a/testcases/kernel/syscalls/prctl/prctl02.c b/testcases/kernel/syscalls/prctl/prctl02.c index 1cd33f88..8d865fb0 100755 --- a/testcases/kernel/syscalls/prctl/prctl02.c +++ b/testcases/kernel/syscalls/prctl/prctl02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * - EINVAL when an invalid value is given for option * - EINVAL when option is PR_SET_PDEATHSIG & arg2 is not zero or a valid * signal number diff --git a/testcases/kernel/syscalls/prctl/prctl03.c b/testcases/kernel/syscalls/prctl/prctl03.c index bac918e8..cd70c39a 100755 --- a/testcases/kernel/syscalls/prctl/prctl03.c +++ b/testcases/kernel/syscalls/prctl/prctl03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test PR_SET_CHILD_SUBREAPER and PR_GET_CHILD_SUBREAPER of prctl(2). * * - If PR_SET_CHILD_SUBREAPER marks a process as a child subreaper, it diff --git a/testcases/kernel/syscalls/prctl/prctl05.c b/testcases/kernel/syscalls/prctl/prctl05.c index dd596630..a014ba89 100755 --- a/testcases/kernel/syscalls/prctl/prctl05.c +++ b/testcases/kernel/syscalls/prctl/prctl05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test PR_GET_NAME and PR_SET_NAME of prctl(2). * * - Set the name of the calling thread, the name can be up to 16 bytes diff --git a/testcases/kernel/syscalls/prctl/prctl06.c b/testcases/kernel/syscalls/prctl/prctl06.c index a9b35b24..087b2935 100755 --- a/testcases/kernel/syscalls/prctl/prctl06.c +++ b/testcases/kernel/syscalls/prctl/prctl06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test PR_GET_NO_NEW_PRIVS and PR_SET_NO_NEW_PRIVS of prctl(2). * * - Return the value of the no_new_privs bit for the calling thread. diff --git a/testcases/kernel/syscalls/prctl/prctl07.c b/testcases/kernel/syscalls/prctl/prctl07.c index dd1d2c06..e7f3f948 100755 --- a/testcases/kernel/syscalls/prctl/prctl07.c +++ b/testcases/kernel/syscalls/prctl/prctl07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test the PR_CAP_AMBIENT of prctl(2). * * Reads or changes the ambient capability set of the calling thread, diff --git a/testcases/kernel/syscalls/prctl/prctl08.c b/testcases/kernel/syscalls/prctl/prctl08.c index f090623b..8c27df38 100755 --- a/testcases/kernel/syscalls/prctl/prctl08.c +++ b/testcases/kernel/syscalls/prctl/prctl08.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test PR_GET_TIMERSLACK and PR_SET_TIMERSLACK of prctl(2). * * - Each thread has two associated timer slack values: a "default" diff --git a/testcases/kernel/syscalls/prctl/prctl09.c b/testcases/kernel/syscalls/prctl/prctl09.c index c0696fcd..5c962318 100755 --- a/testcases/kernel/syscalls/prctl/prctl09.c +++ b/testcases/kernel/syscalls/prctl/prctl09.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This is a timer sample test that timer slack is 200us. */ diff --git a/testcases/kernel/syscalls/prctl/prctl10.c b/testcases/kernel/syscalls/prctl/prctl10.c index b77268c0..d7868f13 100644 --- a/testcases/kernel/syscalls/prctl/prctl10.c +++ b/testcases/kernel/syscalls/prctl/prctl10.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic test to test behaviour of PR_GET_TSC and PR_SET_TSC. * * Set the state of the flag determining whether the timestamp counter can diff --git a/testcases/kernel/syscalls/pread/pread01.c b/testcases/kernel/syscalls/pread/pread01.c index d11ca58f..36a93a3f 100755 --- a/testcases/kernel/syscalls/pread/pread01.c +++ b/testcases/kernel/syscalls/pread/pread01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify the functionality of pread() by writing known data using pwrite() * to the file at various specified offsets and later read from the file from * various specified offsets, comparing the data read against the data written. diff --git a/testcases/kernel/syscalls/pread/pread02.c b/testcases/kernel/syscalls/pread/pread02.c index 04c7d037..d38daa4c 100755 --- a/testcases/kernel/syscalls/pread/pread02.c +++ b/testcases/kernel/syscalls/pread/pread02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the pread syscall. * * - ESPIPE when attempted to read from an unnamed pipe diff --git a/testcases/kernel/syscalls/preadv/preadv.h b/testcases/kernel/syscalls/preadv/preadv.h deleted file mode 100755 index 73466a9a..00000000 --- a/testcases/kernel/syscalls/preadv/preadv.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -* Copyright (c) 2015 Fujitsu Ltd. -* Author: Xiao Yang -* -* 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 -* alone with this program. -*/ - -#ifndef PREADV_H -#define PREADV_H - -#include -#include "config.h" -#include "lapi/syscalls.h" - -#if !defined(HAVE_PREADV) -int preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) -{ - return tst_syscall(__NR_preadv, fd, iov, iovcnt, offset); -} -#endif - -#endif /* RREADV_H */ diff --git a/testcases/kernel/syscalls/preadv/preadv01.c b/testcases/kernel/syscalls/preadv/preadv01.c index 62f9296f..68e8d28c 100755 --- a/testcases/kernel/syscalls/preadv/preadv01.c +++ b/testcases/kernel/syscalls/preadv/preadv01.c @@ -1,17 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2015 Fujitsu Ltd. -* Author: Xiao Yang -*/ + * Copyright (c) 2015 Fujitsu Ltd. + * Author: Xiao Yang + * Copyright (c) Linux Test Project, 2016-2023 + */ -/* -* Test Name: preadv01 -* -* Test Description: -* Testcase to check the basic functionality of the preadv(2). -* Preadv(2) should succeed to read the expected content of data -* and after reading the file, the file offset is not changed. -*/ +/*\ + * Testcase to check the basic functionality of the preadv(2). + * + * Preadv(2) should succeed to read the expected content of data + * and after reading the file, the file offset is not changed. + */ #define _GNU_SOURCE @@ -19,7 +18,7 @@ #include #include "tst_test.h" -#include "preadv.h" +#include "lapi/uio.h" #define CHUNK 64 @@ -38,7 +37,7 @@ static struct tcase { {1, CHUNK*3/2, CHUNK/2, 'b'} }; -void verify_preadv(unsigned int n) +static void verify_preadv(unsigned int n) { int i; char *vec; @@ -81,7 +80,7 @@ void verify_preadv(unsigned int n) "with content '%c' expectedly", tc->size, tc->content); } -void setup(void) +static void setup(void) { char buf[CHUNK]; @@ -94,7 +93,7 @@ void setup(void) SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, sizeof(buf)); } -void cleanup(void) +static void cleanup(void) { if (fd > 0) SAFE_CLOSE(fd); diff --git a/testcases/kernel/syscalls/preadv/preadv02.c b/testcases/kernel/syscalls/preadv/preadv02.c index 500059e4..0dc16f4a 100755 --- a/testcases/kernel/syscalls/preadv/preadv02.c +++ b/testcases/kernel/syscalls/preadv/preadv02.c @@ -1,39 +1,27 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2015-2016 Fujitsu Ltd. -* Author: Xiao Yang -*/ + * Copyright (c) 2015-2016 Fujitsu Ltd. + * Author: Xiao Yang + * Copyright (c) Linux Test Project, 2017-2023 + */ -/* -* Test Name: preadv02 -* -* Description: -* 1) preadv(2) fails if iov_len is invalid. -* 2) preadv(2) fails if the vector count iovcnt is less than zero. -* 3) preadv(2) fails if offset is negative. -* 4) preadv(2) fails when attempts to read into a invalid address. -* 5) preadv(2) fails if file descriptor is invalid. -* 6) preadv(2) fails if file descriptor is not open for reading. -* 7) preadv(2) fails when fd refers to a directory. -* 8) preadv(2) fails if fd is associated with a pipe. -* -* Expected Result: -* 1) preadv(2) should return -1 and set errno to EINVAL. -* 2) preadv(2) should return -1 and set errno to EINVAL. -* 3) preadv(2) should return -1 and set errno to EINVAL. -* 4) preadv(2) should return -1 and set errno to EFAULT. -* 5) preadv(2) should return -1 and set errno to EBADF. -* 6) preadv(2) should return -1 and set errno to EBADF. -* 7) preadv(2) should return -1 and set errno to EISDIR. -* 8) preadv(2) should return -1 and set errno to ESPIPE. -*/ +/*\ + * - EINVAL when iov_len is invalid. + * - EINVAL when the vector count iovcnt is less than zero. + * - EINVAL when offset is negative. + * - EFAULT when attempts to read into a invalid address. + * - EBADF when file descriptor is invalid. + * - EBADF when file descriptor is not open for reading. + * - EISDIR when fd refers to a directory. + * - ESPIPE when fd is associated with a pipe. + */ #define _GNU_SOURCE #include #include #include "tst_test.h" -#include "preadv.h" +#include "lapi/uio.h" #define CHUNK 64 diff --git a/testcases/kernel/syscalls/preadv/preadv03.c b/testcases/kernel/syscalls/preadv/preadv03.c index d4595dda..b6ec0a60 100755 --- a/testcases/kernel/syscalls/preadv/preadv03.c +++ b/testcases/kernel/syscalls/preadv/preadv03.c @@ -2,12 +2,13 @@ /* * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved. * Author: Xiao Yang + * Copyright (c) Linux Test Project, 2019-2023 */ -/* - * Description: +/*\ * Check the basic functionality of the preadv(2) for the file * opened with O_DIRECT in all filesystem. + * * preadv(2) should succeed to read the expected content of data * and after reading the file, the file offset is not changed. */ @@ -19,7 +20,7 @@ #include #include #include "tst_test.h" -#include "preadv.h" +#include "lapi/uio.h" #define MNTPOINT "mntpoint" #define FNAME MNTPOINT"/file" diff --git a/testcases/kernel/syscalls/preadv2/preadv201.c b/testcases/kernel/syscalls/preadv2/preadv201.c index 11097729..60651128 100755 --- a/testcases/kernel/syscalls/preadv2/preadv201.c +++ b/testcases/kernel/syscalls/preadv2/preadv201.c @@ -4,15 +4,15 @@ * Author: Xiao Yang */ -/* - * Description: - * Testcase to check the basic functionality of the preadv2(2). - * 1) If the file offset argument is not -1, preadv2() should succeed - * in reading the expected content of data and the file offset is - * not changed after reading. - * 2) If the file offset argument is -1, preadv2() should succeed in - * reading the expected content of data and the current file offset - * is used and changed after reading. +/*\ + * Verify the basic functionality of the preadv2(2): + * + * 1. If the file offset argument is not -1, preadv2() should succeed + * in reading the expected content of data and the file offset is not + * changed after reading. + * 2. If the file offset argument is -1, preadv2() should succeed in + * reading the expected content of data and the current file offset + * is used and changed after reading. */ #define _GNU_SOURCE @@ -20,7 +20,7 @@ #include #include "tst_test.h" -#include "lapi/preadv2.h" +#include "lapi/uio.h" #define CHUNK 64 diff --git a/testcases/kernel/syscalls/preadv2/preadv202.c b/testcases/kernel/syscalls/preadv2/preadv202.c index 4e1e82eb..c0e3f13a 100755 --- a/testcases/kernel/syscalls/preadv2/preadv202.c +++ b/testcases/kernel/syscalls/preadv2/preadv202.c @@ -4,20 +4,17 @@ * Author: Xiao Yang */ -/* - * Description: - * Check various errnos for preadv2(2). - * 1) preadv2() fails and sets errno to EINVAL if iov_len is invalid. - * 2) preadv2() fails and sets errno to EINVAL if the vector count iovcnt - * is less than zero. - * 3) preadv2() fails and sets errno to EOPNOTSUPP if flag is invalid. - * 4) preadv2() fails and sets errno to EFAULT when attempts to read into - * a invalid address. - * 5) preadv2() fails and sets errno to EBADF if file descriptor is invalid. - * 6) preadv2() fails and sets errno to EBADF if file descriptor is not - * open for reading. - * 7) preadv2() fails and sets errno to EISDIR when fd refers to a directory. - * 8) preadv2() fails and sets errno to ESPIPE if fd is associated with a pipe. +/*\ + * Verify that, preadv2(2) fails and sets errno to + * + * 1. EINVAL if iov_len is invalid. + * 2. EINVAL if the vector count iovcnt is less than zero. + * 3. EOPNOTSUPP if flag is invalid. + * 4. EFAULT when attempting to read into an invalid address. + * 5. EBADF if file descriptor is invalid. + * 6. EBADF if file descriptor is not open for reading. + * 7. EISDIR when fd refers to a directory. + * 8. ESPIPE if fd is associated with a pipe. */ #define _GNU_SOURCE @@ -25,7 +22,7 @@ #include #include "tst_test.h" -#include "lapi/preadv2.h" +#include "lapi/uio.h" #define CHUNK 64 diff --git a/testcases/kernel/syscalls/preadv2/preadv203.c b/testcases/kernel/syscalls/preadv2/preadv203.c index c87deb67..472543e5 100755 --- a/testcases/kernel/syscalls/preadv2/preadv203.c +++ b/testcases/kernel/syscalls/preadv2/preadv203.c @@ -45,7 +45,7 @@ #include "tst_test.h" #include "tst_safe_pthread.h" -#include "lapi/preadv2.h" +#include "lapi/uio.h" #define CHUNK_SZ 4123 #define CHUNKS 60 @@ -278,6 +278,6 @@ static struct tst_test test = { .mntpoint = MNTPOINT, .mount_device = 1, .all_filesystems = 1, - .max_runtime = 60, + .min_runtime = 60, .needs_root = 1, }; diff --git a/testcases/kernel/syscalls/process_madvise/process_madvise.h b/testcases/kernel/syscalls/process_madvise/process_madvise.h index c4570e53..5b227ada 100644 --- a/testcases/kernel/syscalls/process_madvise/process_madvise.h +++ b/testcases/kernel/syscalls/process_madvise/process_madvise.h @@ -54,7 +54,7 @@ static inline void read_address_mapping(unsigned long address, struct addr_mappi if (!found) continue; - if (found && strcmp(line, "VmFlags") >= 0) + if (found && strncmp(line, "VmFlags", 7) == 0) break; if (sscanf(line, "%31[^:]: %d", label, &value) > 0) { diff --git a/testcases/kernel/syscalls/process_madvise/process_madvise01.c b/testcases/kernel/syscalls/process_madvise/process_madvise01.c index 322b4cf6..14ab1d83 100644 --- a/testcases/kernel/syscalls/process_madvise/process_madvise01.c +++ b/testcases/kernel/syscalls/process_madvise/process_madvise01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Allocate anonymous memory pages inside child and reclaim it with * MADV_PAGEOUT. Then check if memory pages have been swapped out by looking * at smaps information. @@ -23,7 +21,9 @@ #include "lapi/syscalls.h" #include "process_madvise.h" -#define MEM_CHILD (1 * TST_MB) +#define MEM_LIMIT (100 * TST_MB) +#define MEMSW_LIMIT (200 * TST_MB) +#define MEM_CHILD (1 * TST_MB) static void **data_ptr; @@ -67,6 +67,12 @@ static void child_alloc(void) static void setup(void) { + SAFE_CG_PRINTF(tst_cg, "memory.max", "%d", MEM_LIMIT); + if (SAFE_CG_HAS(tst_cg, "memory.swap.max")) + SAFE_CG_PRINTF(tst_cg, "memory.swap.max", "%d", MEMSW_LIMIT); + + SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid()); + data_ptr = SAFE_MMAP(NULL, sizeof(void *), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); @@ -123,7 +129,9 @@ static struct tst_test test = { .min_kver = "5.10", .needs_checkpoints = 1, .needs_root = 1, - .min_swap_avail = MEM_CHILD / TST_KB, + .min_mem_avail = 2 * MEM_LIMIT / TST_MB, + .min_swap_avail = 2 * MEM_CHILD / TST_MB, + .needs_cgroup_ctrls = (const char *const []){ "memory", NULL }, .needs_kconfigs = (const char *[]) { "CONFIG_SWAP=y", NULL diff --git a/testcases/kernel/syscalls/pselect/pselect02.c b/testcases/kernel/syscalls/pselect/pselect02.c index 391a31bb..e5396077 100755 --- a/testcases/kernel/syscalls/pselect/pselect02.c +++ b/testcases/kernel/syscalls/pselect/pselect02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that pselect() fails with: * * - EBADF if a file descriptor that was already closed diff --git a/testcases/kernel/syscalls/ptrace/.gitignore b/testcases/kernel/syscalls/ptrace/.gitignore index 01cbc607..1ee6117e 100755 --- a/testcases/kernel/syscalls/ptrace/.gitignore +++ b/testcases/kernel/syscalls/ptrace/.gitignore @@ -3,6 +3,7 @@ /ptrace03 /ptrace04 /ptrace05 +/ptrace06 /ptrace07 /ptrace08 /ptrace09 diff --git a/testcases/kernel/syscalls/ptrace/Makefile b/testcases/kernel/syscalls/ptrace/Makefile index 9ee7b837..d7eca683 100755 --- a/testcases/kernel/syscalls/ptrace/Makefile +++ b/testcases/kernel/syscalls/ptrace/Makefile @@ -1,34 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) Linux Test Project, 2002-2023 # Copyright (c) International Business Machines Corp., 2001 top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk -# - ptrace06 is a broken test ; it hangs the target consistently, chewing up -# CPU. See: `Issue 3 - ptrace06 hung for 7 hours' -- -# http://sourceforge.net/mailarchive/forum.php?thread_name=364299f40910062300s65c43c93w9cccdfe8835c2334%40mail.gmail.com&forum_name=ltp-list -# - simple_tracer is a utility that Mike Frysinger added that shouldn't be -# compiled by default: -# -# gcc -g -O2 -g -O2 -fno-strict-aliasing -pipe -Wall -# -I//scratch/ltp-install6/include -I../../../../include -# -L//scratch/ltp-install6/lib simple_tracer.c -laio -lltp -o -# simple_tracer -# In file included from simple_tracer.c:25: -# syscalls.h:6:23: error: _syscalls.h: No such file or directory -# make[4]: *** [simple_tracer] Error 1 -# make[4]: Leaving directory -# `/scratch/ltp-dev2/ltp/testcases/kernel/syscalls/ptrace' -# make[3]: *** [all] Error 2 -# make[3]: Leaving directory `/scratch/ltp-dev2/ltp/testcases/kernel/syscalls' -# make[2]: *** [all] Error 2 -# make[2]: Leaving directory `/scratch/ltp-dev2/ltp/testcases/kernel' -# make[1]: *** [all] Error 2 -# make[1]: Leaving directory `/scratch/ltp-dev2/ltp/testcases' -# make: *** [testcases-all] Error 2 -# - -FILTER_OUT_MAKE_TARGETS := ptrace06 simple_tracer - include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/ptrace/make_syscall_list.sh b/testcases/kernel/syscalls/ptrace/make_syscall_list.sh deleted file mode 100755 index e5d6d276..00000000 --- a/testcases/kernel/syscalls/ptrace/make_syscall_list.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -set -- $( \ - printf '#include ' | \ - ${CC:-gcc} -E -dD - | \ - awk '$2 ~ /^SYS_/ { sub(/SYS_/,"",$2); print $2; }' -) -printf 'P(%s)\n' "$@" diff --git a/testcases/kernel/syscalls/ptrace/ptrace.h b/testcases/kernel/syscalls/ptrace/ptrace.h deleted file mode 100755 index 4a43f01e..00000000 --- a/testcases/kernel/syscalls/ptrace/ptrace.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * ptrace is a fickle beast and each arch sucks in a different way - */ - -#ifndef __LTP_PTRACE_H__ -#define __LTP_PTRACE_H__ - -#ifdef HAVE_SYS_PTRACE_H -# include -#endif -#ifdef HAVE_SYS_REG_H -# include -#endif -#ifdef __ia64__ /* what a pos */ -# define ia64_fpreg FU_ia64_fpreg -# define pt_all_user_regs FU_pt_all_user_regs -#endif -#ifdef HAVE_ASM_PTRACE_H -# include -#endif -#ifdef HAVE_LINUX_PTRACE_H -# ifndef HAVE_STRUCT_PTRACE_PEEKSIGINFO_ARGS -# include -# endif -#endif -#undef FU_ia64_fpreg -#undef FU_pt_all_user_regs - -#if defined(HAVE_STRUCT_PT_REGS) -typedef struct pt_regs ptrace_regs; -#elif defined(HAVE_STRUCT_USER_REGS_STRUCT) -typedef struct user_regs_struct ptrace_regs; -#endif - -#endif diff --git a/testcases/kernel/syscalls/ptrace/ptrace01.c b/testcases/kernel/syscalls/ptrace/ptrace01.c index 9071bbab..18b00ebf 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace01.c +++ b/testcases/kernel/syscalls/ptrace/ptrace01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Copyright (c) 2019 SUSE LLC @@ -26,8 +26,7 @@ #include #include #include -#include -#include "ptrace.h" +#include #include "tst_test.h" static struct tcase { diff --git a/testcases/kernel/syscalls/ptrace/ptrace02.c b/testcases/kernel/syscalls/ptrace/ptrace02.c index e330f459..a48ef58a 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace02.c +++ b/testcases/kernel/syscalls/ptrace/ptrace02.c @@ -11,9 +11,8 @@ #include #include #include -#include #include -#include "ptrace.h" +#include #include "tst_test.h" uid_t uid; diff --git a/testcases/kernel/syscalls/ptrace/ptrace03.c b/testcases/kernel/syscalls/ptrace/ptrace03.c index b2b3fb49..42ac3e7c 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace03.c +++ b/testcases/kernel/syscalls/ptrace/ptrace03.c @@ -14,9 +14,8 @@ #include #include #include -#include #include -#include "ptrace.h" +#include #include "tst_test.h" static pid_t unused_pid; diff --git a/testcases/kernel/syscalls/ptrace/ptrace04.c b/testcases/kernel/syscalls/ptrace/ptrace04.c index af35fb3a..8f1b5c6c 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace04.c +++ b/testcases/kernel/syscalls/ptrace/ptrace04.c @@ -13,9 +13,7 @@ #include #include #include - -#include -#include "ptrace.h" +#include #include "test.h" #include "spawn_ptrace_child.h" @@ -49,7 +47,7 @@ int TST_TOTAL = 2; void compare_registers(unsigned char poison) { -#ifdef HAVE_STRUCT_PTRACE_REGS +#if defined(HAVE_STRUCT_PTRACE_REGS) && defined(PTRACE_GETREGS) ptrace_regs _pt_regs; size_t i; long ret; diff --git a/testcases/kernel/syscalls/ptrace/ptrace05.c b/testcases/kernel/syscalls/ptrace/ptrace05.c index 54cfa4d7..a44eb417 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace05.c +++ b/testcases/kernel/syscalls/ptrace/ptrace05.c @@ -1,180 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - ****************************************************************************** - * - * ptrace05 - an app which ptraces itself as per arbitrarily specified signals, - * over a user specified range. - * - * Copyright (C) 2009, Ngie Cooper - * - * 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. - * - ****************************************************************************** + * Copyright (c) Linux Test Project, 2009-2019 + * Copyright (C) 2009, Ngie Cooper + * Copyright (c) 2023 Wei Gao + */ + +/*\ + * This test ptraces itself as per arbitrarily specified signals, + * over 0 to SIGRTMAX range. */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include - -#include -#include "ptrace.h" - -#include "test.h" +#include #include "lapi/signal.h" +#include "tst_test.h" -char *TCID = "ptrace05"; -int TST_TOTAL = 0; +static int expect_stop; -int usage(const char *); - -int usage(const char *argv0) +static void test_signal(int signum) { - fprintf(stderr, "usage: %s [start-signum] [end-signum]\n", argv0); - return 1; -} - -int main(int argc, char **argv) -{ - - int end_signum = -1; - int signum; - int start_signum = -1; int status; - pid_t child; - tst_parse_opts(argc, argv, NULL, NULL); + child = SAFE_FORK(); - if (start_signum == -1) { - start_signum = 0; - } - if (end_signum == -1) { - end_signum = SIGRTMAX; + if (!child) { + TST_EXP_PASS_SILENT(ptrace(PTRACE_TRACEME, 0, NULL, NULL)); + tst_res(TDEBUG, "[child] Sending kill(.., %s)", tst_strsig(signum)); + SAFE_KILL(getpid(), signum); + exit(0); } - for (signum = start_signum; signum <= end_signum; signum++) { + SAFE_WAITPID(child, &status, 0); + switch (signum) { + case 0: + if (WIFEXITED(status) + && WEXITSTATUS(status) == 0) { + tst_res(TPASS, + "kill(.., 0) exited with 0, as expected."); + } else { + tst_res(TFAIL, + "kill(.., 0) exited with unexpected %s.", tst_strstatus(status)); + } + break; + case SIGKILL: + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + tst_res(TPASS, "Child killed by SIGKILL"); + else + tst_res(TFAIL, "Child %s", tst_strstatus(status)); + break; + /* All other processes should be stopped. */ + default: + if (WIFSTOPPED(status)) { + tst_res(TDEBUG, "Stopped as expected"); + } else { + tst_res(TFAIL, "Didn't stop as expected. Child %s", tst_strstatus(status)); + expect_stop++; + } + break; + } + + if (signum != 0 && signum != SIGKILL) + SAFE_PTRACE(PTRACE_CONT, child, NULL, NULL); +} + +static void run(void) +{ + int signum = 0; + + for (signum = 0; signum <= SIGRTMAX; signum++) { if (signum >= __SIGRTMIN && signum < SIGRTMIN) continue; - - switch (child = fork()) { - case -1: - tst_brkm(TBROK | TERRNO, NULL, "fork() failed"); - case 0: - - if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != -1) { - tst_resm(TINFO, "[child] Sending kill(.., %d)", - signum); - if (kill(getpid(), signum) < 0) { - tst_resm(TINFO | TERRNO, - "[child] kill(.., %d) failed.", - signum); - } - } else { - - /* - * This won't increment the TST_COUNT var. - * properly, but it'll show up as a failure - * nonetheless. - */ - tst_resm(TFAIL | TERRNO, - "Failed to ptrace(PTRACE_TRACEME, ...) " - "properly"); - - } - /* Shouldn't get here if signum == 0. */ - exit((signum == 0 ? 0 : 2)); - break; - - default: - - waitpid(child, &status, 0); - - switch (signum) { - case 0: - if (WIFEXITED(status) - && WEXITSTATUS(status) == 0) { - tst_resm(TPASS, - "kill(.., 0) exited " - "with 0, as expected."); - } else { - tst_resm(TFAIL, - "kill(.., 0) didn't exit " - "with 0."); - } - break; - case SIGKILL: - if (WIFSIGNALED(status)) { - /* SIGKILL must be uncatchable. */ - if (WTERMSIG(status) == SIGKILL) { - tst_resm(TPASS, - "Killed with SIGKILL, " - "as expected."); - } else { - tst_resm(TPASS, - "Didn't die with " - "SIGKILL (?!) "); - } - } else if (WIFEXITED(status)) { - tst_resm(TFAIL, - "Exited unexpectedly instead " - "of dying with SIGKILL."); - } else if (WIFSTOPPED(status)) { - tst_resm(TFAIL, - "Stopped instead of dying " - "with SIGKILL."); - } - break; - /* All other processes should be stopped. */ - default: - if (WIFSTOPPED(status)) { - tst_resm(TPASS, "Stopped as expected"); - } else { - tst_resm(TFAIL, "Didn't stop as " - "expected."); - if (kill(child, 0)) { - tst_resm(TINFO, - "Is still alive!?"); - } else if (WIFEXITED(status)) { - tst_resm(TINFO, - "Exited normally"); - } else if (WIFSIGNALED(status)) { - tst_resm(TINFO, - "Was signaled with " - "signum=%d", - WTERMSIG(status)); - } - - } - - break; - - } - - } - /* Make sure the child dies a quick and painless death ... */ - kill(child, 9); - + test_signal(signum); } - - tst_exit(); - } + +static struct tst_test test = { + .test_all = run, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/ptrace/ptrace06.c b/testcases/kernel/syscalls/ptrace/ptrace06.c index c0cb3b9b..573a9579 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace06.c +++ b/testcases/kernel/syscalls/ptrace/ptrace06.c @@ -1,202 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * check out-of-bound/unaligned addresses given to + * Copyright (c) 2008 Analog Devices Inc. + * Copyright (c) Linux Test Project, 2008-2023 + * Copyright (c) 2025 Wei Gao + */ + +/*\ + * Check out-of-bound/unaligned addresses given to + * * - {PEEK,POKE}{DATA,TEXT,USER} * - {GET,SET}{,FG}REGS * - {GET,SET}SIGINFO - * - * Copyright (c) 2008 Analog Devices Inc. - * - * Licensed under the GPL-2 or later */ -#define _GNU_SOURCE - -#include -#include -#include #include -#include - -#include -#include "ptrace.h" - -#include "test.h" -#include "spawn_ptrace_child.h" -#include "config.h" +#include +#include "tst_test.h" /* this should be sizeof(struct user), but that info is only found * in the kernel asm/user.h which is not exported to userspace. */ + #if defined(__i386__) -#define SIZEOF_USER 284 +# define SIZEOF_USER 284 #elif defined(__x86_64__) -#define SIZEOF_USER 928 +# define SIZEOF_USER 928 #else -#define SIZEOF_USER 0x1000 /* just pick a big number */ +# define SIZEOF_USER 0x1000 /* just pick a big number */ #endif -char *TCID = "ptrace06"; - -struct test_case_t { +static struct test_case_t { int request; long addr; long data; } test_cases[] = { - { - PTRACE_PEEKDATA,.addr = 0}, { - PTRACE_PEEKDATA,.addr = 1}, { - PTRACE_PEEKDATA,.addr = 2}, { - PTRACE_PEEKDATA,.addr = 3}, { - PTRACE_PEEKDATA,.addr = -1}, { - PTRACE_PEEKDATA,.addr = -2}, { - PTRACE_PEEKDATA,.addr = -3}, { - PTRACE_PEEKDATA,.addr = -4}, { - PTRACE_PEEKTEXT,.addr = 0}, { - PTRACE_PEEKTEXT,.addr = 1}, { - PTRACE_PEEKTEXT,.addr = 2}, { - PTRACE_PEEKTEXT,.addr = 3}, { - PTRACE_PEEKTEXT,.addr = -1}, { - PTRACE_PEEKTEXT,.addr = -2}, { - PTRACE_PEEKTEXT,.addr = -3}, { - PTRACE_PEEKTEXT,.addr = -4}, { - PTRACE_PEEKUSER,.addr = SIZEOF_USER + 1}, { - PTRACE_PEEKUSER,.addr = SIZEOF_USER + 2}, { - PTRACE_PEEKUSER,.addr = SIZEOF_USER + 3}, { - PTRACE_PEEKUSER,.addr = SIZEOF_USER + 4}, { - PTRACE_PEEKUSER,.addr = -1}, { - PTRACE_PEEKUSER,.addr = -2}, { - PTRACE_PEEKUSER,.addr = -3}, { - PTRACE_PEEKUSER,.addr = -4}, { - PTRACE_POKEDATA,.addr = 0}, { - PTRACE_POKEDATA,.addr = 1}, { - PTRACE_POKEDATA,.addr = 2}, { - PTRACE_POKEDATA,.addr = 3}, { - PTRACE_POKEDATA,.addr = -1}, { - PTRACE_POKEDATA,.addr = -2}, { - PTRACE_POKEDATA,.addr = -3}, { - PTRACE_POKEDATA,.addr = -4}, { - PTRACE_POKETEXT,.addr = 0}, { - PTRACE_POKETEXT,.addr = 1}, { - PTRACE_POKETEXT,.addr = 2}, { - PTRACE_POKETEXT,.addr = 3}, { - PTRACE_POKETEXT,.addr = -1}, { - PTRACE_POKETEXT,.addr = -2}, { - PTRACE_POKETEXT,.addr = -3}, { - PTRACE_POKETEXT,.addr = -4}, { - PTRACE_POKEUSER,.addr = SIZEOF_USER + 1}, { - PTRACE_POKEUSER,.addr = SIZEOF_USER + 2}, { - PTRACE_POKEUSER,.addr = SIZEOF_USER + 3}, { - PTRACE_POKEUSER,.addr = SIZEOF_USER + 4}, { - PTRACE_POKEUSER,.addr = -1}, { - PTRACE_POKEUSER,.addr = -2}, { - PTRACE_POKEUSER,.addr = -3}, { - PTRACE_POKEUSER,.addr = -4}, + {PTRACE_PEEKDATA, .addr = 0}, + {PTRACE_PEEKDATA, .addr = 1}, + {PTRACE_PEEKDATA, .addr = 2}, + {PTRACE_PEEKDATA, .addr = 3}, + {PTRACE_PEEKDATA, .addr = -1}, + {PTRACE_PEEKDATA, .addr = -2}, + {PTRACE_PEEKDATA, .addr = -3}, + {PTRACE_PEEKDATA, .addr = -4}, + {PTRACE_PEEKTEXT, .addr = 0}, + {PTRACE_PEEKTEXT, .addr = 1}, + {PTRACE_PEEKTEXT, .addr = 2}, + {PTRACE_PEEKTEXT, .addr = 3}, + {PTRACE_PEEKTEXT, .addr = -1}, + {PTRACE_PEEKTEXT, .addr = -2}, + {PTRACE_PEEKTEXT, .addr = -3}, + {PTRACE_PEEKTEXT, .addr = -4}, + {PTRACE_PEEKUSER, .addr = SIZEOF_USER + 1}, + {PTRACE_PEEKUSER, .addr = SIZEOF_USER + 2}, + {PTRACE_PEEKUSER, .addr = SIZEOF_USER + 3}, + {PTRACE_PEEKUSER, .addr = SIZEOF_USER + 4}, + {PTRACE_PEEKUSER, .addr = -1}, + {PTRACE_PEEKUSER, .addr = -2}, + {PTRACE_PEEKUSER, .addr = -3}, + {PTRACE_PEEKUSER, .addr = -4}, + {PTRACE_POKEDATA, .addr = 0}, + {PTRACE_POKEDATA, .addr = 1}, + {PTRACE_POKEDATA, .addr = 2}, + {PTRACE_POKEDATA, .addr = 3}, + {PTRACE_POKEDATA, .addr = -1}, + {PTRACE_POKEDATA, .addr = -2}, + {PTRACE_POKEDATA, .addr = -3}, + {PTRACE_POKEDATA, .addr = -4}, + {PTRACE_POKETEXT, .addr = 0}, + {PTRACE_POKETEXT, .addr = 1}, + {PTRACE_POKETEXT, .addr = 2}, + {PTRACE_POKETEXT, .addr = 3}, + {PTRACE_POKETEXT, .addr = -1}, + {PTRACE_POKETEXT, .addr = -2}, + {PTRACE_POKETEXT, .addr = -3}, + {PTRACE_POKETEXT, .addr = -4}, + {PTRACE_POKEUSER, .addr = SIZEOF_USER + 1}, + {PTRACE_POKEUSER, .addr = SIZEOF_USER + 2}, + {PTRACE_POKEUSER, .addr = SIZEOF_USER + 3}, + {PTRACE_POKEUSER, .addr = SIZEOF_USER + 4}, + {PTRACE_POKEUSER, .addr = -1}, + {PTRACE_POKEUSER, .addr = -2}, + {PTRACE_POKEUSER, .addr = -3}, + {PTRACE_POKEUSER, .addr = -4}, #ifdef PTRACE_GETREGS - { - PTRACE_GETREGS,.data = 0}, { - PTRACE_GETREGS,.data = 1}, { - PTRACE_GETREGS,.data = 2}, { - PTRACE_GETREGS,.data = 3}, { - PTRACE_GETREGS,.data = -1}, { - PTRACE_GETREGS,.data = -2}, { - PTRACE_GETREGS,.data = -3}, { - PTRACE_GETREGS,.data = -4}, + {PTRACE_GETREGS, .data = 0}, + {PTRACE_GETREGS, .data = 1}, + {PTRACE_GETREGS, .data = 2}, + {PTRACE_GETREGS, .data = 3}, + {PTRACE_GETREGS, .data = -1}, + {PTRACE_GETREGS, .data = -2}, + {PTRACE_GETREGS, .data = -3}, + {PTRACE_GETREGS, .data = -4}, #endif #ifdef PTRACE_GETFGREGS - { - PTRACE_GETFGREGS,.data = 0}, { - PTRACE_GETFGREGS,.data = 1}, { - PTRACE_GETFGREGS,.data = 2}, { - PTRACE_GETFGREGS,.data = 3}, { - PTRACE_GETFGREGS,.data = -1}, { - PTRACE_GETFGREGS,.data = -2}, { - PTRACE_GETFGREGS,.data = -3}, { - PTRACE_GETFGREGS,.data = -4}, + {PTRACE_GETFGREGS, .data = 0}, + {PTRACE_GETFGREGS, .data = 1}, + {PTRACE_GETFGREGS, .data = 2}, + {PTRACE_GETFGREGS, .data = 3}, + {PTRACE_GETFGREGS, .data = -1}, + {PTRACE_GETFGREGS, .data = -2}, + {PTRACE_GETFGREGS, .data = -3}, + {PTRACE_GETFGREGS, .data = -4}, #endif #ifdef PTRACE_SETREGS - { - PTRACE_SETREGS,.data = 0}, { - PTRACE_SETREGS,.data = 1}, { - PTRACE_SETREGS,.data = 2}, { - PTRACE_SETREGS,.data = 3}, { - PTRACE_SETREGS,.data = -1}, { - PTRACE_SETREGS,.data = -2}, { - PTRACE_SETREGS,.data = -3}, { - PTRACE_SETREGS,.data = -4}, + {PTRACE_SETREGS, .data = 0}, + {PTRACE_SETREGS, .data = 1}, + {PTRACE_SETREGS, .data = 2}, + {PTRACE_SETREGS, .data = 3}, + {PTRACE_SETREGS, .data = -1}, + {PTRACE_SETREGS, .data = -2}, + {PTRACE_SETREGS, .data = -3}, + {PTRACE_SETREGS, .data = -4}, #endif #ifdef PTRACE_SETFGREGS - { - PTRACE_SETFGREGS,.data = 0}, { - PTRACE_SETFGREGS,.data = 1}, { - PTRACE_SETFGREGS,.data = 2}, { - PTRACE_SETFGREGS,.data = 3}, { - PTRACE_SETFGREGS,.data = -1}, { - PTRACE_SETFGREGS,.data = -2}, { - PTRACE_SETFGREGS,.data = -3}, { - PTRACE_SETFGREGS,.data = -4}, + {PTRACE_SETFGREGS, .data = 0}, + {PTRACE_SETFGREGS, .data = 1}, + {PTRACE_SETFGREGS, .data = 2}, + {PTRACE_SETFGREGS, .data = 3}, + {PTRACE_SETFGREGS, .data = -1}, + {PTRACE_SETFGREGS, .data = -2}, + {PTRACE_SETFGREGS, .data = -3}, + {PTRACE_SETFGREGS, .data = -4}, #endif #if HAVE_DECL_PTRACE_GETSIGINFO - { - PTRACE_GETSIGINFO,.data = 0}, { - PTRACE_GETSIGINFO,.data = 1}, { - PTRACE_GETSIGINFO,.data = 2}, { - PTRACE_GETSIGINFO,.data = 3}, { - PTRACE_GETSIGINFO,.data = -1}, { - PTRACE_GETSIGINFO,.data = -2}, { - PTRACE_GETSIGINFO,.data = -3}, { - PTRACE_GETSIGINFO,.data = -4}, + {PTRACE_GETSIGINFO, .data = 0}, + {PTRACE_GETSIGINFO, .data = 1}, + {PTRACE_GETSIGINFO, .data = 2}, + {PTRACE_GETSIGINFO, .data = 3}, + {PTRACE_GETSIGINFO, .data = -1}, + {PTRACE_GETSIGINFO, .data = -2}, + {PTRACE_GETSIGINFO, .data = -3}, + {PTRACE_GETSIGINFO, .data = -4}, #endif #if HAVE_DECL_PTRACE_SETSIGINFO - { - PTRACE_SETSIGINFO,.data = 0}, { - PTRACE_SETSIGINFO,.data = 1}, { - PTRACE_SETSIGINFO,.data = 2}, { - PTRACE_SETSIGINFO,.data = 3}, { - PTRACE_SETSIGINFO,.data = -1}, { - PTRACE_SETSIGINFO,.data = -2}, { - PTRACE_SETSIGINFO,.data = -3}, { - PTRACE_SETSIGINFO,.data = -4}, + {PTRACE_SETSIGINFO, .data = 0}, + {PTRACE_SETSIGINFO, .data = 1}, + {PTRACE_SETSIGINFO, .data = 2}, + {PTRACE_SETSIGINFO, .data = 3}, + {PTRACE_SETSIGINFO, .data = -1}, + {PTRACE_SETSIGINFO, .data = -2}, + {PTRACE_SETSIGINFO, .data = -3}, + {PTRACE_SETSIGINFO, .data = -4}, #endif }; -int TST_TOTAL = ARRAY_SIZE(test_cases); +#define SPT(x)[PTRACE_##x] = #x, +static char *strings[] = { + SPT(TRACEME) + SPT(PEEKTEXT) + SPT(PEEKDATA) + SPT(PEEKUSER) + SPT(POKETEXT) + SPT(POKEDATA) + SPT(POKEUSER) +#ifdef PTRACE_GETREGS + SPT(GETREGS) +#endif +#ifdef PTRACE_SETREGS + SPT(SETREGS) +#endif +#ifdef PTRACE_GETSIGINFO + SPT(GETSIGINFO) +#endif +#ifdef PTRACE_SETSIGINFO + SPT(SETSIGINFO) +#endif +#ifdef PTRACE_GETFGREGS + SPT(GETFGREGS) +#endif +#ifdef PTRACE_SETFGREGS + SPT(SETFGREGS) +#endif + SPT(KILL) + SPT(SINGLESTEP) +}; -int main(int argc, char *argv[]) +static void child(void) +{ + SAFE_PTRACE(PTRACE_TRACEME, 0, NULL, NULL); + raise(SIGSTOP); + exit(0); +} + +static void run(void) { size_t i; - long ret; - int saved_errno; + int pid; + int status; + int exp_errnos[] = {EIO, EFAULT}; - tst_parse_opts(argc, argv, NULL, NULL); + pid = SAFE_FORK(); - make_a_baby(argc, argv); + if (!pid) + child(); + + SAFE_WAIT(&status); + + if (!WIFSTOPPED(status)) + tst_brk(TBROK, "child %d was not stopped", pid); for (i = 0; i < ARRAY_SIZE(test_cases); ++i) { struct test_case_t *tc = &test_cases[i]; - errno = 0; - ret = - ptrace(tc->request, pid, (void *)tc->addr, - (void *)tc->data); - saved_errno = errno; - if (ret != -1) - tst_resm(TFAIL, - "ptrace(%s, ..., %li, %li) returned %li instead of -1", - strptrace(tc->request), tc->addr, tc->data, - ret); - else if (saved_errno != EIO && saved_errno != EFAULT) - tst_resm(TFAIL, - "ptrace(%s, ..., %li, %li) expected errno EIO or EFAULT; actual: %i (%s)", - strptrace(tc->request), tc->addr, tc->data, - saved_errno, strerror(saved_errno)); - else - tst_resm(TPASS, - "ptrace(%s, ..., %li, %li) failed as expected", - strptrace(tc->request), tc->addr, tc->data); + TST_EXP_FAIL_ARR(ptrace(tc->request, pid, (void *)tc->addr, + (void *)tc->data), exp_errnos, ARRAY_SIZE(exp_errnos), + "ptrace(%s, ..., %li, %li) failed as expected", + strings[tc->request], tc->addr, tc->data); } - /* hopefully this worked */ - ptrace(PTRACE_KILL, pid, NULL, NULL); + SAFE_PTRACE(PTRACE_CONT, pid, NULL, NULL); - tst_exit(); } + +static struct tst_test test = { + .test_all = run, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/ptrace/ptrace07.c b/testcases/kernel/syscalls/ptrace/ptrace07.c index 362cee54..a7824367 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace07.c +++ b/testcases/kernel/syscalls/ptrace/ptrace07.c @@ -35,9 +35,8 @@ #include #include #include +#include -#include "config.h" -#include "ptrace.h" #include "tst_safe_macros.h" #include "lapi/cpuid.h" diff --git a/testcases/kernel/syscalls/ptrace/ptrace08.c b/testcases/kernel/syscalls/ptrace/ptrace08.c index d17d6b41..754ab23a 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace08.c +++ b/testcases/kernel/syscalls/ptrace/ptrace08.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018 Andrew Lutomirski * Copyright (C) 2020 SUSE LLC diff --git a/testcases/kernel/syscalls/ptrace/ptrace11.c b/testcases/kernel/syscalls/ptrace/ptrace11.c index c5444167..7f08aa99 100755 --- a/testcases/kernel/syscalls/ptrace/ptrace11.c +++ b/testcases/kernel/syscalls/ptrace/ptrace11.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Before kernel 2.6.26 we could not trace init(1) process and ptrace() would * fail with EPERM. This case just checks whether we can trace init(1) process * successfully. @@ -16,9 +14,8 @@ #include #include #include -#include #include -#include "ptrace.h" +#include #include "tst_test.h" static void verify_ptrace(void) diff --git a/testcases/kernel/syscalls/ptrace/simple_tracer.c b/testcases/kernel/syscalls/ptrace/simple_tracer.c deleted file mode 100755 index ae1af7c2..00000000 --- a/testcases/kernel/syscalls/ptrace/simple_tracer.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * simple example ptrace() code to help build basis for other tests - * - * Copyright (c) 2009 Analog Devices Inc. - * - * Licensed under the GPL-2 or later - */ - -#define _GNU_SOURCE - -#include - -#include -#include -#include -#include -#include -#include -#include -#include "ptrace.h" - -#include "test.h" -#include "spawn_ptrace_child.h" - -#include "syscalls.h" - -char *TCID = "simple_tracer"; -int TST_TOTAL = 0; - -#define _decode(name, val) \ -({ \ - if (sizeof(long) == 4) \ - printf(name ":%08lx ", val); \ - else if (sizeof(long) == 8) \ - printf(name ":%016lx ", val); \ - else \ - printf(name ":%lx ", val); \ - val; \ -}) -#define decode(reg) _decode(#reg, pt->reg) -#define decode_user(name, offset) \ - _decode(name, vptrace(PTRACE_PEEKUSER, pid, offset, NULL)); -#define decode_sysnum(nr) printf("%s ", get_sysnum(nr)) -static void decode_regs(struct pt_regs *pt) -{ -#if defined(__bfin__) - long nr = decode_user("orig_p0", PT_ORIG_P0); - decode(p0); - decode(r0); - decode(r1); - decode(r2); - decode(r3); - decode(r4); - decode(r5); - decode_sysnum(nr); - puts(""); -#elif defined(__i386__) - long nr = decode_user("orig_eax", 4 * ORIG_EAX); - decode(eax); - decode(ebx); - decode(ecx); - decode(edx); - decode(esi); - decode(edi); - decode(ebp); - decode_sysnum(nr); - puts(""); -#elif defined(__x86_64__) - long nr = decode_user("orig_rax", 8 * ORIG_RAX); - decode(rax); - decode(rbx); - decode(rcx); - decode(rdx); - decode(rsi); - decode(rdi); - decode(rbp); - decode_sysnum(nr); - puts(""); -#elif defined(__sparc__) -#define G1 u_regs[0] -#define G2 u_regs[1] -#define G3 u_regs[2] -#define G4 u_regs[3] -#define G5 u_regs[4] -#define G6 u_regs[5] -#define G7 u_regs[6] -#define O0 u_regs[7] -#define O1 u_regs[8] -#define O2 u_regs[9] -#define O3 u_regs[10] -#define O4 u_regs[11] -#define O5 u_regs[12] -#define O6 u_regs[13] -#define O7 u_regs[14] - decode(G1); - decode(G2); - decode(G3); - decode(G4); - decode(G5); - decode(G6); - decode(G7); - decode(O0); - decode(O1); - decode(O2); - decode(O3); - decode(O4); - decode(O5); - decode(O6); - decode(O7); - decode_sysnum(pt->G1); - puts(""); -#else -#warning "no idea how to decode your arch" - puts("no idea how to decode your arch"); -#endif -} - -int main(int argc, char *argv[]) -{ - struct pt_regs pt_regs; - long ret; - int status; - - make_a_baby(argc, argv); - - while (1) { - ret = vptrace(PTRACE_GETREGS, pid, NULL, &pt_regs); - if (ret) - break; - decode_regs(&pt_regs); - - ret = vptrace(PTRACE_SYSCALL, pid, NULL, NULL); - if (ret) - break; - - if (waitpid(pid, &status, 0) == -1) - break; - } - - /* hopefully this worked */ - vptrace(PTRACE_KILL, pid, NULL, NULL); - - tst_exit(); -} diff --git a/testcases/kernel/syscalls/ptrace/syscalls.h b/testcases/kernel/syscalls/ptrace/syscalls.h deleted file mode 100755 index 2d9c5ceb..00000000 --- a/testcases/kernel/syscalls/ptrace/syscalls.h +++ /dev/null @@ -1,17 +0,0 @@ -const struct sysnums { - long nr; - const char *snr; -} sysnums[] = { -#define P(NR) { .nr = SYS_##NR, .snr = #NR, }, -#include "_syscalls.h" -#undef P -}; - -const char *get_sysnum(long nr) -{ - int i; - for (i = 0; i < ARRAY_SIZE(sysnums); ++i) - if (sysnums[i].nr == nr) - break; - return i == ARRAY_SIZE(sysnums) ? "???" : sysnums[i].snr; -} diff --git a/testcases/kernel/syscalls/pwrite/pwrite01.c b/testcases/kernel/syscalls/pwrite/pwrite01.c index 8f7ad2fb..0c3689ce 100755 --- a/testcases/kernel/syscalls/pwrite/pwrite01.c +++ b/testcases/kernel/syscalls/pwrite/pwrite01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify the functionality of pwrite() by writing known data using pwrite() * to the file at various specified offsets and later read from the file from * various specified offsets, comparing the data written aganist the data diff --git a/testcases/kernel/syscalls/pwrite/pwrite02.c b/testcases/kernel/syscalls/pwrite/pwrite02.c index 9a40eddc..d9ffac48 100755 --- a/testcases/kernel/syscalls/pwrite/pwrite02.c +++ b/testcases/kernel/syscalls/pwrite/pwrite02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test basic error handling of the pwrite syscall. * * - ESPIPE when attempted to write to an unnamed pipe diff --git a/testcases/kernel/syscalls/pwrite/pwrite03.c b/testcases/kernel/syscalls/pwrite/pwrite03.c index ae572c1e..406c59d2 100755 --- a/testcases/kernel/syscalls/pwrite/pwrite03.c +++ b/testcases/kernel/syscalls/pwrite/pwrite03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests for a special case NULL buffer with size 0 is expected to return 0. */ diff --git a/testcases/kernel/syscalls/pwrite/pwrite04.c b/testcases/kernel/syscalls/pwrite/pwrite04.c index 85995539..c22e444f 100755 --- a/testcases/kernel/syscalls/pwrite/pwrite04.c +++ b/testcases/kernel/syscalls/pwrite/pwrite04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test the pwrite() system call with O_APPEND. * * Writing 2k data to the file, close it and reopen it with O_APPEND. diff --git a/testcases/kernel/syscalls/pwritev/pwritev.h b/testcases/kernel/syscalls/pwritev/pwritev.h deleted file mode 100755 index 833160dd..00000000 --- a/testcases/kernel/syscalls/pwritev/pwritev.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -* Copyright (c) 2015 Fujitsu Ltd. -* Author: Xiao Yang -* -* 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 -* alone with this program. -*/ - -#ifndef PWRITEV_H -#define PWRITEV_H - -#include -#include "config.h" -#include "lapi/syscalls.h" - -#if !defined(HAVE_PWRITEV) -int pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) -{ - return tst_syscall(__NR_pwritev, fd, iov, iovcnt, offset); -} -#endif - -#endif /* PWRITEV_H */ diff --git a/testcases/kernel/syscalls/pwritev/pwritev01.c b/testcases/kernel/syscalls/pwritev/pwritev01.c index 66358f7c..0aa5cb47 100755 --- a/testcases/kernel/syscalls/pwritev/pwritev01.c +++ b/testcases/kernel/syscalls/pwritev/pwritev01.c @@ -1,23 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2015 Fujitsu Ltd. -* Author: Xiao Yang -*/ + * Copyright (c) 2015 Fujitsu Ltd. + * Author: Xiao Yang + * Copyright (c) Linux Test Project, 2016-2023 + */ -/* -* Test Name: pwritev01 -* -* Test Description: -* Testcase to check the basic functionality of the pwritev(2). -* pwritev(2) should succeed to write the expected content of data -* and after writing the file, the file offset is not changed. -*/ +/*\ + * Testcase to check the basic functionality of the pwritev(2). + * + * pwritev(2) should succeed to write the expected content of data + * and after writing the file, the file offset is not changed. + */ #define _GNU_SOURCE #include #include #include "tst_test.h" -#include "pwritev.h" +#include "lapi/uio.h" #include "tst_safe_prw.h" #define CHUNK 64 diff --git a/testcases/kernel/syscalls/pwritev/pwritev02.c b/testcases/kernel/syscalls/pwritev/pwritev02.c index 0881b756..b1ba4235 100755 --- a/testcases/kernel/syscalls/pwritev/pwritev02.c +++ b/testcases/kernel/syscalls/pwritev/pwritev02.c @@ -1,37 +1,26 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) 2015-2016 Fujitsu Ltd. -* Author: Xiao Yang -*/ + * Copyright (c) 2015-2016 Fujitsu Ltd. + * Author: Xiao Yang + * Copyright (c) Linux Test Project, 2017-2023 + */ -/* -* Test Name: pwritev02 -* -* Description: -* 1) pwritev(2) fails if iov_len is invalid. -* 2) pwritev(2) fails if the vector count iovcnt is less than zero. -* 3) pwritev(2) fails if offset is negative. -* 4) pwritev(2) fails when attempts to write from a invalid address -* 5) pwritev(2) fails if file descriptor is invalid. -* 6) pwritev(2) fails if file descriptor is not open for writing. -* 7) pwritev(2) fails if fd is associated with a pipe. -* -* Expected Result: -* 1) pwritev(2) should return -1 and set errno to EINVAL. -* 2) pwritev(2) should return -1 and set errno to EINVAL. -* 3) pwritev(2) should return -1 and set errno to EINVAL. -* 4) pwritev(2) should return -1 and set errno to EFAULT. -* 5) pwritev(2) should return -1 and set errno to EBADF. -* 6) pwritev(2) should return -1 and set errno to EBADF. -* 7) pwritev(2) should return -1 and set errno to ESPIPE. -*/ +/*\ + * - EINVAL when iov_len is invalid. + * - EINVAL when the vector count iovcnt is less than zero. + * - EINVAL when offset is negative. + * - EFAULT when attempts to write from a invalid address + * - EBADF when file descriptor is invalid. + * - EBADF when file descriptor is not open for writing. + * - ESPIPE when fd is associated with a pipe. + */ #define _GNU_SOURCE #include #include #include "tst_test.h" -#include "pwritev.h" +#include "lapi/uio.h" #define CHUNK 64 diff --git a/testcases/kernel/syscalls/pwritev/pwritev03.c b/testcases/kernel/syscalls/pwritev/pwritev03.c index 8b91de33..2655697d 100755 --- a/testcases/kernel/syscalls/pwritev/pwritev03.c +++ b/testcases/kernel/syscalls/pwritev/pwritev03.c @@ -2,12 +2,13 @@ /* * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved. * Author: Xiao Yang + * Copyright (c) Linux Test Project, 2019-2023 */ -/* - * Description: +/*\ * Check the basic functionality of the pwritev(2) for the file * opened with O_DIRECT in all filesystem. + * * pwritev(2) should succeed to write the expected content of data * and after writing the file, the file offset is not changed. */ @@ -19,7 +20,7 @@ #include #include #include "tst_test.h" -#include "pwritev.h" +#include "lapi/uio.h" #include "tst_safe_prw.h" #define MNTPOINT "mntpoint" @@ -90,7 +91,7 @@ static void verify_direct_pwritev(unsigned int n) static void setup(void) { int dev_fd, ret; - + dev_fd = SAFE_OPEN(tst_device->dev, O_RDWR); SAFE_IOCTL(dev_fd, BLKSSZGET, &ret); SAFE_CLOSE(dev_fd); diff --git a/testcases/kernel/syscalls/pwritev2/pwritev201.c b/testcases/kernel/syscalls/pwritev2/pwritev201.c index eba45b7d..b74bf918 100755 --- a/testcases/kernel/syscalls/pwritev2/pwritev201.c +++ b/testcases/kernel/syscalls/pwritev2/pwritev201.c @@ -3,15 +3,16 @@ * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. * Author: Jinhui Huang */ -/* - * Description: + +/*\ * Testcase to check the basic functionality of the pwritev2(2). - * 1) If the file offset argument is not -1, pwritev2() should succeed - * in writing the expected content of data and the file offset is - * not changed after writing. - * 2) If the file offset argument is -1, pwritev2() should succeed in - * writing the expected content of data and the current file offset - * is used and changed after writing. + * + * - If the file offset argument is not -1, pwritev2() should succeed + * in writing the expected content of data and the file offset is + * not changed after writing. + * - If the file offset argument is -1, pwritev2() should succeed in + * writing the expected content of data and the current file offset + * is used and changed after writing. */ #define _GNU_SOURCE @@ -19,7 +20,7 @@ #include #include "tst_test.h" -#include "lapi/pwritev2.h" +#include "lapi/uio.h" #include "tst_safe_prw.h" #define CHUNK 64 diff --git a/testcases/kernel/syscalls/pwritev2/pwritev202.c b/testcases/kernel/syscalls/pwritev2/pwritev202.c index b44620cc..30345fa7 100755 --- a/testcases/kernel/syscalls/pwritev2/pwritev202.c +++ b/testcases/kernel/syscalls/pwritev2/pwritev202.c @@ -3,19 +3,20 @@ * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. * Author: Jinhui Huang */ -/* - * Description: + +/*\ * Check various errnos for pwritev2(2). - * 1) pwritev2() fails and sets errno to EINVAL if iov_len is invalid. - * 2) pwritev2() fails and sets errno to EINVAL if the vector count iovcnt is - * less than zero. - * 3) pwritev2() fails and sets errno to EOPNOTSUPP if flag is invalid. - * 4) pwritev2() fails and sets errno to EFAULT when writing data from invalid - * address. - * 5) pwritev2() fails and sets errno to EBADF if file descriptor is invalid. - * 6) pwritev2() fails and sets errno to EBADF if file descriptor is open for - * reading. - * 7) pwritev2() fails and sets errno to ESPIPE if fd is associated with a pipe. + * + * - pwritev2() fails and sets errno to EINVAL if iov_len is invalid. + * - pwritev2() fails and sets errno to EINVAL if the vector count iovcnt is + * less than zero. + * - pwritev2() fails and sets errno to EOPNOTSUPP if flag is invalid. + * - pwritev2() fails and sets errno to EFAULT when writing data from invalid + * address. + * - pwritev2() fails and sets errno to EBADF if file descriptor is invalid. + * - pwritev2() fails and sets errno to EBADF if file descriptor is open for + * reading. + * - pwritev2() fails and sets errno to ESPIPE if fd is associated with a pipe. */ #define _GNU_SOURCE @@ -23,7 +24,7 @@ #include #include "tst_test.h" -#include "lapi/pwritev2.h" +#include "lapi/uio.h" #define CHUNK 64 diff --git a/testcases/kernel/syscalls/quotactl/quotactl01.c b/testcases/kernel/syscalls/quotactl/quotactl01.c index 36ec93ed..9dcf74ce 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl01.c +++ b/testcases/kernel/syscalls/quotactl/quotactl01.c @@ -7,7 +7,6 @@ */ /*\ - * [Description] * This testcases checks that quotactl(2) on ext4 filesystem succeeds to: * * - turn on quota with Q_QUOTAON flag for user @@ -216,9 +215,14 @@ static struct tst_test test = { .test = verify_quota, .tcnt = ARRAY_SIZE(tcases), .mount_device = 1, - .dev_fs_type = "ext4", + .filesystems = (struct tst_fs []) { + { + .type = "ext4", + .mnt_data = "usrquota,grpquota", + }, + {} + }, .mntpoint = MNTPOINT, - .mnt_data = "usrquota,grpquota", .needs_cmds = (const char *const []) { "quotacheck", NULL diff --git a/testcases/kernel/syscalls/quotactl/quotactl02.c b/testcases/kernel/syscalls/quotactl/quotactl02.c index d9c4f9b2..d6f54708 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl02.c +++ b/testcases/kernel/syscalls/quotactl/quotactl02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This testcases checks that quotactl(2) on xfs filesystem succeeds to: * * - turn off xfs quota and get xfs quota off status for user @@ -150,9 +148,14 @@ static struct tst_test test = { .test = verify_quota, .tcnt = ARRAY_SIZE(tcases), .mount_device = 1, - .dev_fs_type = "xfs", + .filesystems = (struct tst_fs []) { + { + .type = "xfs", + .mnt_data = "usrquota,grpquota", + }, + {} + }, .mntpoint = MNTPOINT, - .mnt_data = "usrquota,grpquota", .setup = setup, .cleanup = cleanup, .test_variants = QUOTACTL_SYSCALL_VARIANTS, diff --git a/testcases/kernel/syscalls/quotactl/quotactl03.c b/testcases/kernel/syscalls/quotactl/quotactl03.c index 22f5496b..3ee15289 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl03.c +++ b/testcases/kernel/syscalls/quotactl/quotactl03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * quotactl(2) with XGETNEXTQUOTA looks for the next active quota for an user * equal or higher to a given ID, in this test the ID is specified to a value * close to UINT_MAX(max value of unsigned int). When reaching the upper limit @@ -83,9 +81,14 @@ static struct tst_test test = { }, .test_all = verify_quota, .mount_device = 1, - .dev_fs_type = "xfs", + .filesystems = (struct tst_fs []) { + { + .type = "xfs", + .mnt_data = "usrquota", + }, + {} + }, .mntpoint = MNTPOINT, - .mnt_data = "usrquota", .test_variants = QUOTACTL_SYSCALL_VARIANTS, .tags = (const struct tst_tag[]) { {"linux-git", "657bdfb7f5e6"}, diff --git a/testcases/kernel/syscalls/quotactl/quotactl04.c b/testcases/kernel/syscalls/quotactl/quotactl04.c index a57e6be6..d2d7b3f3 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl04.c +++ b/testcases/kernel/syscalls/quotactl/quotactl04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This testcase checks that quotactl(2) on ext4 filesystem succeeds to: * * - turn on quota with Q_QUOTAON flag for project @@ -32,7 +30,7 @@ #define FMTID QFMT_VFS_V1 static int32_t fmt_id = FMTID; -static int test_id, mount_flag; +static int test_id; static struct dqblk set_dq = { .dqb_bsoftlimit = 100, .dqb_valid = QIF_BLIMITS @@ -98,12 +96,8 @@ static struct tcase { static void setup(void) { - const char *const fs_opts[] = {"-I 256", "-O quota,project", NULL}; - quotactl_info(); - SAFE_MKFS(tst_device->dev, tst_device->fs_type, fs_opts, NULL); - SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL); - mount_flag = 1; + fd = SAFE_OPEN(MNTPOINT, O_RDONLY); TEST(do_quotactl(fd, QCMD(Q_GETNEXTQUOTA, PRJQUOTA), tst_device->dev, @@ -116,8 +110,6 @@ static void cleanup(void) { if (fd > -1) SAFE_CLOSE(fd); - if (mount_flag && tst_umount(MNTPOINT)) - tst_res(TWARN | TERRNO, "umount(%s)", MNTPOINT); } static void verify_quota(unsigned int n) @@ -161,8 +153,16 @@ static struct tst_test test = { .tcnt = ARRAY_SIZE(tcases), .setup = setup, .cleanup = cleanup, - .needs_device = 1, - .dev_fs_type = "ext4", + .mount_device = 1, + .filesystems = (struct tst_fs []) { + { + .type = "ext4", + .mkfs_opts = (const char *const[]) { + "-I 256", "-O quota,project", NULL + }, + }, + {} + }, .mntpoint = MNTPOINT, .test_variants = QUOTACTL_SYSCALL_VARIANTS, .needs_cmds = (const char *[]) { diff --git a/testcases/kernel/syscalls/quotactl/quotactl05.c b/testcases/kernel/syscalls/quotactl/quotactl05.c index ac75cee3..e9f99d96 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl05.c +++ b/testcases/kernel/syscalls/quotactl/quotactl05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This testcases checks that quotactl(2) on xfs filesystem succeeds to: * * - turn off xfs quota and get xfs quota off status for project @@ -113,9 +111,14 @@ static struct tst_test test = { .test = verify_quota, .tcnt = ARRAY_SIZE(tcases), .mount_device = 1, - .dev_fs_type = "xfs", + .filesystems = (struct tst_fs []) { + { + .type = "xfs", + .mnt_data = "prjquota", + }, + {} + }, .mntpoint = MNTPOINT, - .mnt_data = "prjquota", .setup = setup, .cleanup = cleanup, .test_variants = QUOTACTL_SYSCALL_VARIANTS, diff --git a/testcases/kernel/syscalls/quotactl/quotactl06.c b/testcases/kernel/syscalls/quotactl/quotactl06.c index 74a098a8..110a3aa9 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl06.c +++ b/testcases/kernel/syscalls/quotactl/quotactl06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the quotactl syscall with visible quota files * (cover two formats, vfsv0 and vfsv1): * @@ -219,10 +217,15 @@ static struct tst_test test = { }, .tcnt = ARRAY_SIZE(tcases), .test = verify_quotactl, - .dev_fs_type = "ext4", + .filesystems = (struct tst_fs []) { + { + .type = "ext4", + .mnt_data = "usrquota", + }, + {} + }, .mntpoint = MNTPOINT, .mount_device = 1, - .mnt_data = "usrquota", .needs_cmds = (const char *const []) { "quotacheck", NULL diff --git a/testcases/kernel/syscalls/quotactl/quotactl07.c b/testcases/kernel/syscalls/quotactl/quotactl07.c index 34ff2705..66505a30 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl07.c +++ b/testcases/kernel/syscalls/quotactl/quotactl07.c @@ -5,14 +5,14 @@ */ /*\ - * [Description] - * * This is not only a functional test but also a error test for Q_XQUOTARM. * * It is a regresstion test for kernel commit 3dd4d40b4208 * ("xfs: Sanity check flags of Q_XQUOTARM call"). */ +#define _GNU_SOURCE +#include #include #include #include @@ -83,7 +83,10 @@ static struct tst_test test = { }, .test_all = verify_quota, .format_device = 1, - .dev_fs_type = "xfs", + .filesystems = (struct tst_fs []) { + {.type = "xfs"}, + {} + }, .mntpoint = MNTPOINT, .test_variants = QUOTACTL_SYSCALL_VARIANTS, .tags = (const struct tst_tag[]) { diff --git a/testcases/kernel/syscalls/quotactl/quotactl08.c b/testcases/kernel/syscalls/quotactl/quotactl08.c index 0fabb51a..63087345 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl08.c +++ b/testcases/kernel/syscalls/quotactl/quotactl08.c @@ -5,7 +5,6 @@ */ /*\ - * [Description] * This testcases checks that quotactl(2) on ext4 filesystem succeeds to: * * - turn on quota with Q_QUOTAON flag for user @@ -44,7 +43,7 @@ #define MNTPOINT "mntpoint" static int32_t fmt_id = QFMT_VFS_V1; -static int test_id, mount_flag; +static int test_id; static struct dqblk set_dq = { .dqb_bsoftlimit = 100, .dqb_valid = QIF_BLIMITS @@ -153,14 +152,8 @@ static struct tcase { static void setup(void) { - const char *const fs_opts[] = { "-O quota", NULL}; - quotactl_info(); - SAFE_MKFS(tst_device->dev, tst_device->fs_type, fs_opts, NULL); - SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL); - mount_flag = 1; - fd = SAFE_OPEN(MNTPOINT, O_RDONLY); TEST(do_quotactl(fd, QCMD(Q_GETNEXTQUOTA, USRQUOTA), tst_device->dev, 0, (void *) &res_ndq)); @@ -172,8 +165,6 @@ static void cleanup(void) { if (fd > -1) SAFE_CLOSE(fd); - if (mount_flag && tst_umount(MNTPOINT)) - tst_res(TWARN | TERRNO, "umount(%s)", MNTPOINT); } static void verify_quota(unsigned int n) @@ -216,8 +207,16 @@ static struct tst_test test = { .test = verify_quota, .tcnt = ARRAY_SIZE(tcases), .mntpoint = MNTPOINT, - .dev_fs_type = "ext4", - .needs_device = 1, + .filesystems = (struct tst_fs []) { + { + .type = "ext4", + .mkfs_opts = (const char *const[]){ + "-O quota", NULL + }, + }, + {} + }, + .mount_device = 1, .setup = setup, .cleanup = cleanup, .test_variants = QUOTACTL_SYSCALL_VARIANTS, diff --git a/testcases/kernel/syscalls/quotactl/quotactl09.c b/testcases/kernel/syscalls/quotactl/quotactl09.c index 9a03bff5..67366634 100755 --- a/testcases/kernel/syscalls/quotactl/quotactl09.c +++ b/testcases/kernel/syscalls/quotactl/quotactl09.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests basic error handling of the quotactl syscall without visible quota files * (use quotactl and quotactl_fd syscall): * @@ -176,8 +174,13 @@ static struct tst_test test = { }, .tcnt = ARRAY_SIZE(tcases), .test = verify_quotactl, - .dev_fs_opts = (const char *const[]){"-O quota", NULL}, - .dev_fs_type = "ext4", + .filesystems = (struct tst_fs []) { + { + .type = "ext4", + .mkfs_opts = (const char *const[]){"-O quota", NULL}, + }, + {} + }, .mntpoint = MNTPOINT, .mount_device = 1, .needs_root = 1, diff --git a/testcases/kernel/syscalls/read/read02.c b/testcases/kernel/syscalls/read/read02.c index 9199a95f..3b4af164 100755 --- a/testcases/kernel/syscalls/read/read02.c +++ b/testcases/kernel/syscalls/read/read02.c @@ -52,9 +52,7 @@ static struct tcase { } tcases[] = { {&badfd, &bufaddr, 1, EBADF}, {&fd2, &bufaddr, 1, EISDIR}, -#ifndef UCLINUX {&fd3, &outside_buf, 1, EFAULT}, -#endif {&fd4, &addr4, 1, EINVAL}, {&fd4, &addr5, 4096, EINVAL}, }; @@ -98,10 +96,8 @@ static void setup(void) fd3 = SAFE_OPEN("test_file", O_RDWR); -#if !defined(UCLINUX) outside_buf = SAFE_MMAP(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); -#endif addr4 = SAFE_MEMALIGN(getpagesize(), (4096 * 10)); addr5 = addr4 + 1; diff --git a/testcases/kernel/syscalls/read/read03.c b/testcases/kernel/syscalls/read/read03.c index ee92a329..bb2fc522 100755 --- a/testcases/kernel/syscalls/read/read03.c +++ b/testcases/kernel/syscalls/read/read03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Testcase to check if read() successfully sets errno to EAGAIN when read from * a pipe (fifo, opened in O_NONBLOCK mode) without writing to it. */ diff --git a/testcases/kernel/syscalls/read/read04.c b/testcases/kernel/syscalls/read/read04.c index 154cbf47..f7d9a65d 100755 --- a/testcases/kernel/syscalls/read/read04.c +++ b/testcases/kernel/syscalls/read/read04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Testcase to check if read() returns the number of bytes read correctly. */ diff --git a/testcases/kernel/syscalls/readahead/readahead01.c b/testcases/kernel/syscalls/readahead/readahead01.c index bdef7945..890e4801 100755 --- a/testcases/kernel/syscalls/readahead/readahead01.c +++ b/testcases/kernel/syscalls/readahead/readahead01.c @@ -1,16 +1,15 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2012 Linux Test Project, Inc. + * Copyright (C) 2023-2024 Cyril Hrubis */ /*\ - * [Description] - * * Verify that readahead() syscall fails with: * * - EBADF when fd is not a valid file descriptor or is not open for reading. * - EINVAL when fd does not refer to a file type to which readahead() - * can be applied. + * can be applied. */ #define _GNU_SOURCE #include @@ -29,44 +28,47 @@ #if defined(__NR_readahead) static void test_bad_fd(void) -{ - char tempname[PATH_MAX] = "readahead01_XXXXXX"; - int fd; - - tst_res(TINFO, "%s -1", __func__); - TST_EXP_FAIL(readahead(-1, 0, getpagesize()), EBADF); - - tst_res(TINFO, "%s O_WRONLY", __func__); - fd = mkstemp(tempname); - if (fd == -1) - tst_res(TFAIL | TERRNO, "mkstemp failed"); - SAFE_CLOSE(fd); - fd = SAFE_OPEN(tempname, O_WRONLY); - TST_EXP_FAIL(readahead(fd, 0, getpagesize()), EBADF); - SAFE_CLOSE(fd); - unlink(tempname); -} - -static void test_invalid_fd(void) { int fd[2]; - tst_res(TINFO, "%s pipe", __func__); + TST_EXP_FAIL(readahead(-1, 0, getpagesize()), EBADF, + "readahead() with fd = -1"); + SAFE_PIPE(fd); - TST_EXP_FAIL(readahead(fd[0], 0, getpagesize()), EINVAL); SAFE_CLOSE(fd[0]); SAFE_CLOSE(fd[1]); - tst_res(TINFO, "%s socket", __func__); - fd[0] = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); - TST_EXP_FAIL(readahead(fd[0], 0, getpagesize()), EINVAL); - SAFE_CLOSE(fd[0]); + TST_EXP_FAIL(readahead(fd[0], 0, getpagesize()), EBADF, + "readahead() with invalid fd"); +} + +static void test_invalid_fd(struct tst_fd *fd) +{ + + switch (fd->type) { + /* These succeed */ + case TST_FD_FILE: + case TST_FD_MEMFD: + case TST_FD_MEMFD_SECRET: + case TST_FD_PROC_MAPS: + case TST_FD_PIDFD: + return; + default: + break; + } + + int exp_errnos[] = {EBADF, EINVAL, ESPIPE}; + + TST_EXP_FAIL_ARR(readahead(fd->fd, 0, getpagesize()), exp_errnos, + ARRAY_SIZE(exp_errnos), "readahead() on %s", tst_fd_desc(fd)); } static void test_readahead(void) { test_bad_fd(); - test_invalid_fd(); + + TST_FD_FOREACH(fd) + test_invalid_fd(&fd); } static void setup(void) diff --git a/testcases/kernel/syscalls/readahead/readahead02.c b/testcases/kernel/syscalls/readahead/readahead02.c index dc03c593..f007db18 100755 --- a/testcases/kernel/syscalls/readahead/readahead02.c +++ b/testcases/kernel/syscalls/readahead/readahead02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Linux Test Project, Inc. */ @@ -318,19 +318,6 @@ static void test_readahead(unsigned int n) tst_res(TCONF, "Page cache on your system is too small " "to hold whole testfile."); } - - /* - * The time consuming of readahead quite depending on the platform IO - * speed, sometime test timeout when the default max_runtime is used up. - * - * readahead02.c:221: TINFO: Test #2: POSIX_FADV_WILLNEED on file - * readahead02.c:285: TINFO: read_testfile(0) took: 26317623 usec - * readahead02.c:286: TINFO: read_testfile(1) took: 26101484 usec - * - * Here raise the maximum runtime dynamically. - */ - if ((tc+1)->readahead) - tst_set_max_runtime(test.max_runtime + (usec + usec_ra) / 1000000); } @@ -381,7 +368,7 @@ static void setup(void) { if (opt_fsizestr) { testfile_size = SAFE_STRTOL(opt_fsizestr, 1, INT_MAX); - tst_set_max_runtime(1 + testfile_size / (DEFAULT_FILESIZE/32)); + tst_set_runtime(1 + testfile_size / (DEFAULT_FILESIZE/32)); } if (access(PROC_IO_FNAME, F_OK)) @@ -422,7 +409,7 @@ static struct tst_test test = { }, .test = test_readahead, .tcnt = ARRAY_SIZE(tcases), - .max_runtime = 30, + .timeout = 60, .tags = (const struct tst_tag[]) { {"linux-git", "b833a3660394"}, {"linux-git", "5b910bd615ba"}, diff --git a/testcases/kernel/syscalls/readdir/readdir01.c b/testcases/kernel/syscalls/readdir/readdir01.c index 1bf70fd1..96db6899 100755 --- a/testcases/kernel/syscalls/readdir/readdir01.c +++ b/testcases/kernel/syscalls/readdir/readdir01.c @@ -14,14 +14,14 @@ */ /*\ - * [Description] - * * The test for the readdir(2) system call. * Create n files and check that readdir() finds n files */ #include #include "tst_test.h" +#define MNTPOINT "mntpoint" + static const char prefix[] = "readdirfile"; static int nfiles = 10; @@ -32,7 +32,7 @@ static void setup(void) int fd; for (i = 0; i < nfiles; i++) { - sprintf(fname, "%s_%d", prefix, i); + sprintf(fname, "%s/%s_%d", MNTPOINT, prefix, i); fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700); SAFE_WRITE(SAFE_WRITE_ALL, fd, "hello\n", 6); SAFE_CLOSE(fd); @@ -45,7 +45,7 @@ static void verify_readdir(void) DIR *test_dir; struct dirent *ent; - test_dir = SAFE_OPENDIR("."); + test_dir = SAFE_OPENDIR(MNTPOINT); while ((ent = SAFE_READDIR(test_dir))) { if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) continue; @@ -66,5 +66,8 @@ static void verify_readdir(void) static struct tst_test test = { .setup = setup, .test_all = verify_readdir, - .needs_tmpdir = 1, + .needs_root = 1, + .all_filesystems = 1, + .mount_device = 1, + .mntpoint = MNTPOINT }; diff --git a/testcases/kernel/syscalls/readdir/readdir21.c b/testcases/kernel/syscalls/readdir/readdir21.c index 7308c8bc..bb508a4b 100755 --- a/testcases/kernel/syscalls/readdir/readdir21.c +++ b/testcases/kernel/syscalls/readdir/readdir21.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that readdir will fail with: * * - ENOENT when passed a fd to a deleted directory @@ -20,9 +18,10 @@ #include "lapi/syscalls.h" #include "lapi/readdir.h" -#define TEST_DIR "test_dir" -#define TEST_DIR4 "test_dir4" -#define TEST_FILE "test_file" +#define MNTPOINT "mntpoint" +#define TEST_DIR MNTPOINT "/test_dir" +#define TEST_DIR4 MNTPOINT "/test_dir4" +#define TEST_FILE MNTPOINT "/test_file" #define DIR_MODE 0755 static unsigned int del_dir_fd, file_fd; @@ -75,5 +74,8 @@ static struct tst_test test = { .tcnt = ARRAY_SIZE(tcases), .setup = setup, .test = verify_readdir, - .needs_tmpdir = 1, + .needs_root = 1, + .all_filesystems = 1, + .mount_device = 1, + .mntpoint = MNTPOINT }; diff --git a/testcases/kernel/syscalls/readlink/readlink01.c b/testcases/kernel/syscalls/readlink/readlink01.c index 389ba1fe..a721436e 100755 --- a/testcases/kernel/syscalls/readlink/readlink01.c +++ b/testcases/kernel/syscalls/readlink/readlink01.c @@ -4,10 +4,10 @@ * Ported to LTP: Wayne Boyer */ -/* - * Test Description : - * Testcase to check the basic functionality of the readlink(2), - * readlink() will succeed to read the contents of the symbolic link. +/*\ + * Tests basic functionality of readlink(2). + * + * - readlink() will succeed to read the contents of the symbolic link */ #include diff --git a/testcases/kernel/syscalls/readlink/readlink03.c b/testcases/kernel/syscalls/readlink/readlink03.c index 01ff304d..5c7b5e34 100755 --- a/testcases/kernel/syscalls/readlink/readlink03.c +++ b/testcases/kernel/syscalls/readlink/readlink03.c @@ -4,25 +4,20 @@ * Ported to LTP: Wayne Boyer */ -/* - * Test Description : - * 1) readlink(2) returns -1 and sets errno to EACCES if search/write - * permission is denied in the directory where the symbolic link - * resides. - * 2) readlink(2) returns -1 and sets errno to EINVAL if the buffer size - * is not positive. - * 3) readlink(2) returns -1 and sets errno to EINVAL if the specified - * file is not a symbolic link file. - * 4) readlink(2) returns -1 and sets errno to ENAMETOOLONG if the - * pathname component of symbolic link is too long (ie, > PATH_MAX). - * 5) readlink(2) returns -1 and sets errno to ENOENT if the component of - * symbolic link points to an empty string. - * 6) readlink(2) returns -1 and sets errno to ENOTDIR if a component of - * the path prefix is not a directory. - * 7) readlink(2) returns -1 and sets errno to ELOOP if too many symbolic - * links were encountered in translating the pathname. - * 8) readlink(2) returns -1 and sets errno to EFAULT if buf outside the - * process allocated address space. +/*\ + * Verify that, readlink(2) returns -1 and sets errno to + * + * - EACCES if search/write permission is denied in the directory where the + * symbolic link esides. + * - EINVAL if the buffer size is not positive. + * - EINVAL if the specified file is not a symbolic link file. + * - ENAMETOOLONG if the pathname component of symbolic link is too long + * (ie, > PATH_MAX). + * - ENOENT if the component of symbolic link points to an empty string. + * - ENOTDIR if a component of the path prefix is not a directory. + * - ELOOP if too many symbolic links were encountered in translating the + * pathname. + * - EFAULT if buf outside the process allocated address space. */ #include diff --git a/testcases/kernel/syscalls/readlinkat/readlinkat01.c b/testcases/kernel/syscalls/readlinkat/readlinkat01.c index b1214c3a..c3ff8f9b 100755 --- a/testcases/kernel/syscalls/readlinkat/readlinkat01.c +++ b/testcases/kernel/syscalls/readlinkat/readlinkat01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Check the basic functionality of the readlinkat() system call. * * - readlinkat() passes if dirfd is directory file descriptor @@ -66,10 +64,7 @@ static void verify_readlinkat(unsigned int i) static void setup(void) { - char *tmpdir = tst_get_tmpdir(); - - abspath = tst_aprintf("%s/" TEST_SYMLINK, tmpdir); - free(tmpdir); + abspath = tst_tmpdir_genpath(TEST_SYMLINK); file_fd = SAFE_OPEN(TEST_FILE, O_CREAT, 0600); SAFE_SYMLINK(TEST_FILE, TEST_SYMLINK); diff --git a/testcases/kernel/syscalls/readlinkat/readlinkat02.c b/testcases/kernel/syscalls/readlinkat/readlinkat02.c index 64afb898..e2eaaed7 100755 --- a/testcases/kernel/syscalls/readlinkat/readlinkat02.c +++ b/testcases/kernel/syscalls/readlinkat/readlinkat02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * - readlinkat() fails with EINVAL if the bufsiz is 0. * * - readlinkat() fails with EINVAL if the named file is not a symbolic link. diff --git a/testcases/kernel/syscalls/readv/readv02.c b/testcases/kernel/syscalls/readv/readv02.c index c09e69bc..2ba23d4c 100755 --- a/testcases/kernel/syscalls/readv/readv02.c +++ b/testcases/kernel/syscalls/readv/readv02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Tests readv() failures: * * - EINVAL the sum of the iov_len values overflows an ssize_t value diff --git a/testcases/kernel/syscalls/realpath/realpath01.c b/testcases/kernel/syscalls/realpath/realpath01.c index c0381e9c..9c8e85a9 100755 --- a/testcases/kernel/syscalls/realpath/realpath01.c +++ b/testcases/kernel/syscalls/realpath/realpath01.c @@ -3,12 +3,18 @@ * Copyright (C) 2018 Petr Vorel * Copyright (C) 2018 Michael Moese * - * cve-2018-1000001 realpath buffer underflow * Based on the reproducer posted upstream so other copyrights may apply. * Author: Dmitry V. Levin * LTP conversion from glibc source: Petr Vorel */ +/*\ + * CVE-2018-1000001 realpath buffer underflow. + * + * Based on glibc test io/tst-getcwd-abspath.c: + * https://sourceware.org/git/?p=glibc.git;a=commit;h=52a713fdd0a30e1bd79818e2e3c4ab44ddca1a94. + */ + #include "tst_test.h" #include @@ -24,16 +30,7 @@ static void setup(void) static void run(void) { - TESTPTR(realpath(".", NULL)); - - if (TST_ERR != ENOENT) { - tst_res(TFAIL | TTERRNO, "returned unexpected errno"); - } else if (TST_RET_PTR != NULL) { - tst_res(TFAIL, "syscall didn't return NULL: '%s'", - (char *)TST_RET_PTR); - } else { - tst_res(TPASS, "bug not reproduced"); - } + TST_EXP_FAIL_PTR_NULL(realpath(".", NULL), ENOENT); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/reboot/reboot01.c b/testcases/kernel/syscalls/reboot/reboot01.c index 646ddf61..28fae8bb 100755 --- a/testcases/kernel/syscalls/reboot/reboot01.c +++ b/testcases/kernel/syscalls/reboot/reboot01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Linux Test Project, 2006-2021 * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test of libc wrapper of reboot(2) system call. * * Test LINUX_REBOOT_CMD_CAD_ON, LINUX_REBOOT_CMD_CAD_OFF commands, diff --git a/testcases/kernel/syscalls/reboot/reboot02.c b/testcases/kernel/syscalls/reboot/reboot02.c index 671e984d..5682b78e 100755 --- a/testcases/kernel/syscalls/reboot/reboot02.c +++ b/testcases/kernel/syscalls/reboot/reboot02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Linux Test Project, 2009-2021 * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. @@ -6,7 +6,6 @@ */ /*\ - * [Description] * Test whether libc wrapper of reboot(2) system call returns appropriate * error number for invalid cmd parameter or invalid user. */ diff --git a/testcases/kernel/syscalls/recv/recv01.c b/testcases/kernel/syscalls/recv/recv01.c index 2f09864a..bb257835 100755 --- a/testcases/kernel/syscalls/recv/recv01.c +++ b/testcases/kernel/syscalls/recv/recv01.c @@ -87,13 +87,10 @@ struct test_case_t { /* test case structure */ 0, 0, 0, buf, sizeof(buf), 0, -1, ENOTSOCK, setup0, cleanup0, "invalid socket"} , -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ { PF_INET, SOCK_STREAM, 0, (void *)-1, sizeof(buf), 0, -1, EFAULT, setup1, cleanup1, "invalid recv buffer"} , -#endif { PF_INET, SOCK_STREAM, 0, buf, sizeof(buf), MSG_OOB, -1, EINVAL, setup1, cleanup1, "invalid MSG_OOB flag set"} @@ -105,33 +102,17 @@ struct test_case_t { /* test case structure */ int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); -#ifdef UCLINUX -static char *argv0; -#endif - int main(int argc, char *argv[]) { int lc; tst_parse_opts(argc, argv, NULL, NULL); -#ifdef UCLINUX - argv0 = argv[0]; - maybe_run_child(&do_child, "d", &sfd); -#endif setup(); for (lc = 0; TEST_LOOPING(lc); ++lc) { tst_count = 0; for (testno = 0; testno < TST_TOTAL; ++testno) { - if ((tst_kvercmp(3, 17, 0) < 0) - && (tdat[testno].flags & MSG_ERRQUEUE) - && (tdat[testno].type & SOCK_STREAM)) { - tst_resm(TCONF, "skip MSG_ERRQUEUE test, " - "it's supported from 3.17"); - continue; - } - tdat[testno].setup(); TEST(recv(s, tdat[testno].buf, tdat[testno].buflen, tdat[testno].flags)); @@ -240,15 +221,9 @@ pid_t start_server(struct sockaddr_in *sin0) } SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen); - switch ((pid = FORK_OR_VFORK())) { + switch ((pid = tst_fork())) { case 0: /* child */ -#ifdef UCLINUX - if (self_exec(argv0, "d", sfd) < 0) - tst_brkm(TBROK | TERRNO, cleanup, - "server self_exec failed"); -#else do_child(); -#endif break; case -1: tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); diff --git a/testcases/kernel/syscalls/recvfrom/recvfrom01.c b/testcases/kernel/syscalls/recvfrom/recvfrom01.c index 853d1cb9..6ce9f1bd 100755 --- a/testcases/kernel/syscalls/recvfrom/recvfrom01.c +++ b/testcases/kernel/syscalls/recvfrom/recvfrom01.c @@ -126,34 +126,17 @@ struct test_case_t { /* test case structure */ int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); -#ifdef UCLINUX -static char *argv0; -#endif - int main(int argc, char *argv[]) { int lc; tst_parse_opts(argc, argv, NULL, NULL); -#ifdef UCLINUX - argv0 = argv[0]; - maybe_run_child(&do_child, "d", &sfd); -#endif - setup(); for (lc = 0; TEST_LOOPING(lc); ++lc) { tst_count = 0; for (testno = 0; testno < TST_TOTAL; ++testno) { - if ((tst_kvercmp(3, 17, 0) < 0) - && (tdat[testno].flags & MSG_ERRQUEUE) - && (tdat[testno].type & SOCK_STREAM)) { - tst_resm(TCONF, "skip MSG_ERRQUEUE test, " - "it's supported from 3.17"); - continue; - } - tdat[testno].setup(); TEST(recvfrom(s, tdat[testno].buf, tdat[testno].buflen, tdat[testno].flags, tdat[testno].from, @@ -269,15 +252,9 @@ pid_t start_server(struct sockaddr_in *sin0) } SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen); - switch ((pid = FORK_OR_VFORK())) { + switch ((pid = tst_fork())) { case 0: /* child */ -#ifdef UCLINUX - if (self_exec(argv0, "d", sfd) < 0) { - tst_brkm(TBROK, cleanup, "server self_exec failed"); - } -#else do_child(); -#endif break; case -1: tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); diff --git a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c index fb21ea1e..14606208 100755 --- a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c +++ b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c @@ -47,7 +47,7 @@ static struct test_case tcase[] = { .desc = "bad message vector address", .fd = &receive_sockfd, .exp_errno = EFAULT, - .msg_vec = (void*)&bad_addr, + .msg_vec = (void *)&bad_addr, }, { .desc = "negative seconds in timeout", @@ -74,23 +74,54 @@ static struct test_case tcase[] = { } }; -static void do_test(unsigned int i) +static void verify_recvmmsg(unsigned int i, void *timeout) { struct time64_variants *tv = &variants[tst_variant]; struct test_case *tc = &tcase[i]; - void *timeout; ts.type = tv->ts_type; tst_ts_set_sec(&ts, tc->tv_sec); tst_ts_set_nsec(&ts, tc->tv_nsec); - if (tc->bad_ts_addr) - timeout = bad_addr; - else - timeout = tst_ts_get(&ts); - TST_EXP_FAIL2(tv->recvmmsg(*tc->fd, *tc->msg_vec, VLEN, 0, timeout), - tc->exp_errno, "recvmmsg() %s", tc->desc); + tc->exp_errno, "recvmmsg() %s", tc->desc); +} + +static void test_bad_addr(unsigned int i) +{ + struct time64_variants *tv = &variants[tst_variant]; + void *timeout = bad_addr; + pid_t pid; + int status; + + pid = SAFE_FORK(); + if (!pid) { + verify_recvmmsg(i, timeout); + exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFEXITED(status) && !WEXITSTATUS(status)) + return; + + if (tv->ts_type == TST_LIBC_TIMESPEC && + WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) { + tst_res(TPASS, "Child killed by expected signal"); + return; + } + + tst_res(TFAIL, "Child %s", tst_strstatus(status)); +} + +static void do_test(unsigned int i) +{ + struct test_case *tc = &tcase[i]; + + if (tc->bad_ts_addr) + test_bad_addr(i); + else + verify_recvmmsg(i, tst_ts_get(&ts)); } static void setup(void) @@ -139,6 +170,7 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .test_variants = ARRAY_SIZE(variants), + .forks_child = 1, .bufs = (struct tst_buffers []) { {&iov, .iov_sizes = (int[]){1, -1}}, {&msg, .size = VLEN * sizeof(*msg)}, diff --git a/testcases/kernel/syscalls/recvmsg/recvmsg01.c b/testcases/kernel/syscalls/recvmsg/recvmsg01.c index 80c1b3aa..378be8ca 100755 --- a/testcases/kernel/syscalls/recvmsg/recvmsg01.c +++ b/testcases/kernel/syscalls/recvmsg/recvmsg01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that recvmsg() returns the proper errno for various failure cases. */ @@ -220,13 +218,6 @@ static void run(unsigned int n) struct tcase *tc = &tcases[n]; int ret = tc->exp_errno ? -1 : 0; - if ((tst_kvercmp(3, 17, 0) < 0) - && (tc->flags & MSG_ERRQUEUE) - && (tc->type & SOCK_STREAM)) { - tst_res(TCONF, "MSG_ERRQUEUE requires kernel >= 3.17"); - return; - } - setup_all(); tc->setup(n); diff --git a/testcases/kernel/syscalls/remap_file_pages/remap_file_pages01.c b/testcases/kernel/syscalls/remap_file_pages/remap_file_pages01.c index 09143a2d..8a401fe0 100755 --- a/testcases/kernel/syscalls/remap_file_pages/remap_file_pages01.c +++ b/testcases/kernel/syscalls/remap_file_pages/remap_file_pages01.c @@ -85,6 +85,7 @@ #include #include #include +#include #include "test.h" /*LTP Specific Include File */ @@ -92,6 +93,7 @@ #define WINDOW_START 0x48000000 static int page_sz; +static int granula; size_t page_words; size_t cache_pages; size_t cache_sz; @@ -140,10 +142,10 @@ static void test_nonlinear(int fd) char *data = NULL; int i, j, repeat = 2; - for (i = 0; i < (int)cache_pages; i++) { + for (i = 0; i < (int)cache_pages; i += granula) { char *page = cache_contents + i * page_sz; - for (j = 0; j < (int)page_words; j++) + for (j = 0; j < (int)page_words * granula; j++) page[j] = i; } @@ -164,24 +166,24 @@ static void test_nonlinear(int fd) } again: - for (i = 0; i < (int)window_pages; i += 2) { + for (i = 0; i < (int)window_pages; i += 2 * granula) { char *page = data + i * page_sz; - if (remap_file_pages(page, page_sz * 2, 0, - (window_pages - i - 2), 0) == -1) { + if (remap_file_pages(page, 2 * MMAP_GRANULARITY, 0, + (window_pages - i - 2 * granula), 0) == -1) { tst_resm(TFAIL | TERRNO, "remap_file_pages error for page=%p, " - "page_sz=%d, window_pages=%zu", - page, (page_sz * 2), (window_pages - i - 2)); + "remap_sz=%d, window_pages=%zu", + page, 2 * MMAP_GRANULARITY, (window_pages - i - 2 * granula)); cleanup(data); } } - for (i = 0; i < (int)window_pages; i++) { + for (i = 0, j = 0; i < (int)window_pages; i += granula, j++) { /* * Double-check the correctness of the mapping: */ - if (i & 1) { + if (j & 1) { if (data[i * page_sz] != ((int)window_pages) - i) { tst_resm(TFAIL, "hm, mapped incorrect data, " @@ -191,12 +193,12 @@ again: cleanup(data); } } else { - if (data[i * page_sz] != ((int)window_pages) - i - 2) { + if (data[i * page_sz] != ((int)window_pages) - i - 2 * granula) { tst_resm(TFAIL, "hm, mapped incorrect data, " - "data[%d]=%d, (window_pages-%d-2)=%zu", + "data[%d]=%d, (window_pages-%d-2 * min_pages)=%zu", (i * page_sz), data[i * page_sz], i, - (window_pages - i - 2)); + (window_pages - i - 2 * granula)); cleanup(data); } } @@ -223,13 +225,15 @@ void setup(void) page_words = page_sz; + granula = MMAP_GRANULARITY / page_sz; + /* Set the cache size */ - cache_pages = 1024; + cache_pages = 1024 * granula; cache_sz = cache_pages * page_sz; cache_contents = malloc(cache_sz * sizeof(char)); /* Set the window size */ - window_pages = 16; + window_pages = 16 * granula; window_sz = window_pages * page_sz; sprintf(fname, "/dev/shm/cache_%d", getpid()); diff --git a/testcases/kernel/syscalls/remap_file_pages/remap_file_pages02.c b/testcases/kernel/syscalls/remap_file_pages/remap_file_pages02.c index d296022c..858651fa 100755 --- a/testcases/kernel/syscalls/remap_file_pages/remap_file_pages02.c +++ b/testcases/kernel/syscalls/remap_file_pages/remap_file_pages02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) Ricardo Salveti de Araujo , 2007 * diff --git a/testcases/kernel/syscalls/rename/.gitignore b/testcases/kernel/syscalls/rename/.gitignore index f95cf7d2..d17b80f0 100755 --- a/testcases/kernel/syscalls/rename/.gitignore +++ b/testcases/kernel/syscalls/rename/.gitignore @@ -11,3 +11,4 @@ /rename12 /rename13 /rename14 +/rename15 diff --git a/testcases/kernel/syscalls/rename/rename01.c b/testcases/kernel/syscalls/rename/rename01.c index 159341d0..2bbb70b2 100755 --- a/testcases/kernel/syscalls/rename/rename01.c +++ b/testcases/kernel/syscalls/rename/rename01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify rename() when the newpath file or directory does not exist. */ diff --git a/testcases/kernel/syscalls/rename/rename03.c b/testcases/kernel/syscalls/rename/rename03.c index 652fa3bd..f0c277cb 100755 --- a/testcases/kernel/syscalls/rename/rename03.c +++ b/testcases/kernel/syscalls/rename/rename03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify rename(2) functions correctly when the newpath * file or directory (empty) exists. */ diff --git a/testcases/kernel/syscalls/rename/rename04.c b/testcases/kernel/syscalls/rename/rename04.c index 598ecd6f..44b4d37d 100755 --- a/testcases/kernel/syscalls/rename/rename04.c +++ b/testcases/kernel/syscalls/rename/rename04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename() fails with EEXIST or ENOTEMPTY when * newpath is a non-empty directory. */ diff --git a/testcases/kernel/syscalls/rename/rename05.c b/testcases/kernel/syscalls/rename/rename05.c index 7894a921..2567a525 100755 --- a/testcases/kernel/syscalls/rename/rename05.c +++ b/testcases/kernel/syscalls/rename/rename05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename(2) fails with EISDIR when * oldpath is not a directory and newpath is an existing directory. */ diff --git a/testcases/kernel/syscalls/rename/rename06.c b/testcases/kernel/syscalls/rename/rename06.c index 82665d1c..06ca8bf1 100755 --- a/testcases/kernel/syscalls/rename/rename06.c +++ b/testcases/kernel/syscalls/rename/rename06.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename(2) fails with EINVAL when * an attempt is made to make a directory a subdirectory of itself. */ diff --git a/testcases/kernel/syscalls/rename/rename07.c b/testcases/kernel/syscalls/rename/rename07.c index 51338dbf..dabba77f 100755 --- a/testcases/kernel/syscalls/rename/rename07.c +++ b/testcases/kernel/syscalls/rename/rename07.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename(2) fails with ENOTDIR, when * oldpath is a directory and newpath exists but is not a directory. * diff --git a/testcases/kernel/syscalls/rename/rename08.c b/testcases/kernel/syscalls/rename/rename08.c index 8a9a9b34..c4cd3f48 100755 --- a/testcases/kernel/syscalls/rename/rename08.c +++ b/testcases/kernel/syscalls/rename/rename08.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename(2) fails with EFAULT, when * oldpath or newpath points outside of accessible address space. */ diff --git a/testcases/kernel/syscalls/rename/rename09.c b/testcases/kernel/syscalls/rename/rename09.c index 368a436c..202efa1d 100755 --- a/testcases/kernel/syscalls/rename/rename09.c +++ b/testcases/kernel/syscalls/rename/rename09.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that renaming/moving a file from directory where the current user does * not have write permissions fails with EACCES. */ @@ -27,14 +25,12 @@ #define PERMS 0700 static uid_t orig_uid, test_users[2]; -static char *tmpdir; static void setup(void) { umask(0); orig_uid = getuid(); tst_get_uids(test_users, 0, 2); - tmpdir = tst_get_tmpdir(); } static void run(void) @@ -54,18 +50,12 @@ static void run(void) /* Cleanup between loops */ SAFE_SETEUID(orig_uid); - tst_purge_dir(tmpdir); -} - -static void cleanup(void) -{ - free(tmpdir); + tst_purge_dir(tst_tmpdir_path()); } static struct tst_test test = { .test_all = run, .setup = setup, - .cleanup = cleanup, .needs_root = 1, .needs_tmpdir = 1, }; diff --git a/testcases/kernel/syscalls/rename/rename10.c b/testcases/kernel/syscalls/rename/rename10.c index 444f6536..129cecf4 100755 --- a/testcases/kernel/syscalls/rename/rename10.c +++ b/testcases/kernel/syscalls/rename/rename10.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename(2) fails with ENAMETOOLONG, when * oldpath or newpath is too long. */ @@ -18,7 +16,13 @@ #define MNT_POINT "mntpoint" #define TEMP_FILE "tmpfile" -static char long_path[NAME_MAX + 1] = {[0 ... NAME_MAX] = 'a'}; +/* Path longer than PATH_MAX: fails the syscall right away (getname() fails) */ +static char long_path[PATH_MAX + 1] = {[0 ... PATH_MAX] = 'a'}; +/* + * Path fitting in PATH_MAX, but with an excessively long file name: rejected + * by the underlying filesystem + */ +static char long_name[PATH_MAX] = {[0 ... PATH_MAX - 2] = 'a', [PATH_MAX - 1] = '\0'}; static void setup(void) { @@ -30,6 +34,8 @@ static void run(void) { TST_EXP_FAIL(rename(TEMP_FILE, long_path), ENAMETOOLONG); + TST_EXP_FAIL(rename(TEMP_FILE, long_name), + ENAMETOOLONG); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/rename/rename12.c b/testcases/kernel/syscalls/rename/rename12.c index d6e1ccbe..3e717f24 100755 --- a/testcases/kernel/syscalls/rename/rename12.c +++ b/testcases/kernel/syscalls/rename/rename12.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename() fails with EPERM or EACCES when the directory * containing oldpath has the sticky bit (S_ISVTX) set and the caller * is not privileged. diff --git a/testcases/kernel/syscalls/rename/rename13.c b/testcases/kernel/syscalls/rename/rename13.c index 51490db7..069971d1 100755 --- a/testcases/kernel/syscalls/rename/rename13.c +++ b/testcases/kernel/syscalls/rename/rename13.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that rename() does nothing and returns a success status when * oldpath and newpath are existing hard links referring to the same file. */ diff --git a/testcases/kernel/syscalls/rename/rename14.c b/testcases/kernel/syscalls/rename/rename14.c index 726b35ce..c0e96cef 100755 --- a/testcases/kernel/syscalls/rename/rename14.c +++ b/testcases/kernel/syscalls/rename/rename14.c @@ -89,14 +89,14 @@ int main(int argc, char *argv[]) parent_pid = getpid(); tst_tmpdir(); - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid < 0) tst_brkm(TBROK, NULL, "fork() returned %d", pid); if (pid == 0) dochild1(); kidpid[0] = pid; - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid < 0) { (void)kill(kidpid[0], SIGTERM); (void)unlink("./rename14"); diff --git a/testcases/kernel/syscalls/rename/rename15.c b/testcases/kernel/syscalls/rename/rename15.c new file mode 100644 index 00000000..1ef58073 --- /dev/null +++ b/testcases/kernel/syscalls/rename/rename15.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Authors: David Fenner, Jon Hendrickson + * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com + */ + +/*\ + * This test suite validates the behavior of the `rename()` system call on + * symbolic links under three scenarios: + * + * - rename a symlink pointing to an existing file and verifies that the + * symlink's inode and device number remain unchanged. + * + * - rename a symlink pointing to a non-existent path, ensuring that the + * original symlink remains unaffected. + * + * - rename a symlink pointing to a created file, confirming that the new + * symlink points to the correct file. + */ + +#include "tst_test.h" +#include "tst_tmpdir.h" + +#define MNTPOINT "mnt" +#define OLDNAME MNTPOINT"/msymlink0" +#define NEWNAME MNTPOINT"/asymlink0" +#define OBJNAME MNTPOINT"/object" + +static char *tmpdir; +static char *oldname; +static char *newname; +static char *objname; + +static void test_existing(void) +{ + tst_res(TINFO, "Test rename() on symlink pointing to an existent path"); + + struct stat buff_stat; + struct stat oldsym_stat; + struct stat newsym_stat; + + SAFE_SYMLINK(tmpdir, oldname); + SAFE_STAT(oldname, &oldsym_stat); + + SAFE_RENAME(oldname, newname); + + TST_EXP_PASS(lstat(newname, &buff_stat)); + TST_EXP_FAIL(lstat(oldname, &buff_stat), ENOENT); + + SAFE_STAT(newname, &newsym_stat); + TST_EXP_EQ_LI(oldsym_stat.st_ino, newsym_stat.st_ino); + TST_EXP_EQ_LI(oldsym_stat.st_dev, newsym_stat.st_dev); + + SAFE_UNLINK(newname); +} + +static void test_non_existing(void) +{ + tst_res(TINFO, "Test rename() on symlink pointing to a non-existent path"); + + struct stat path_stat; + + SAFE_SYMLINK("this_path_doesnt_exist", oldname); + TST_EXP_FAIL(stat(oldname, &path_stat), ENOENT); + TST_EXP_PASS(lstat(oldname, &path_stat)); + + SAFE_RENAME(oldname, newname); + + TST_EXP_FAIL(lstat(oldname, &path_stat), ENOENT); + TST_EXP_PASS(lstat(newname, &path_stat)); + TST_EXP_FAIL(stat(newname, &path_stat), ENOENT); + + SAFE_UNLINK(newname); +} + +static void test_creat(void) +{ + tst_res(TINFO, "Test rename() on symlink pointing to a path created lately"); + + struct stat path_stat; + + SAFE_SYMLINK(objname, oldname); + TST_EXP_FAIL(stat(oldname, &path_stat), ENOENT); + TST_EXP_PASS(lstat(oldname, &path_stat)); + + tst_res(TINFO, "Create object file"); + + int fd; + + fd = SAFE_CREAT(objname, 0700); + if (fd >= 0) + SAFE_CLOSE(fd); + + SAFE_RENAME(oldname, newname); + + TST_EXP_PASS(lstat(newname, &path_stat)); + TST_EXP_PASS(stat(newname, &path_stat)); + + TST_EXP_FAIL(lstat(oldname, &path_stat), ENOENT); + TST_EXP_PASS(stat(objname, &path_stat)); + + SAFE_UNLINK(objname); + SAFE_UNLINK(newname); +} + +static void run(void) +{ + test_existing(); + test_creat(); + test_non_existing(); +} + +static void setup(void) +{ + tmpdir = tst_tmpdir_path(); + oldname = tst_tmpdir_genpath(OLDNAME); + newname = tst_tmpdir_genpath(NEWNAME); + objname = tst_tmpdir_genpath(OBJNAME); +} + +static struct tst_test test = { + .setup = setup, + .test_all = run, + .all_filesystems = 1, + .mntpoint = MNTPOINT, + .format_device = 1, + .needs_root = 1, +}; diff --git a/testcases/kernel/syscalls/renameat/renameat01.c b/testcases/kernel/syscalls/renameat/renameat01.c index 3de10356..c318a797 100755 --- a/testcases/kernel/syscalls/renameat/renameat01.c +++ b/testcases/kernel/syscalls/renameat/renameat01.c @@ -50,7 +50,6 @@ #include "test.h" #include "safe_macros.h" #include "lapi/fcntl.h" -#include "lapi/renameat.h" #define MNTPOINT "mntpoint" #define TESTDIR "testdir" diff --git a/testcases/kernel/syscalls/renameat2/renameat201.c b/testcases/kernel/syscalls/renameat2/renameat201.c index 9832b1cd..23ed5758 100755 --- a/testcases/kernel/syscalls/renameat2/renameat201.c +++ b/testcases/kernel/syscalls/renameat2/renameat201.c @@ -100,11 +100,6 @@ int main(int ac, char **av) static void setup(void) { - if ((tst_kvercmp(3, 15, 0)) < 0) { - tst_brkm(TCONF, NULL, - "This test can only run on kernels that are 3.15. and higher"); - } - tst_tmpdir(); fs_type = tst_fs_type(cleanup, "."); diff --git a/testcases/kernel/syscalls/renameat2/renameat202.c b/testcases/kernel/syscalls/renameat2/renameat202.c index 0c145702..88db0476 100755 --- a/testcases/kernel/syscalls/renameat2/renameat202.c +++ b/testcases/kernel/syscalls/renameat2/renameat202.c @@ -78,11 +78,6 @@ int main(int ac, char **av) static void setup(void) { - if ((tst_kvercmp(3, 15, 0)) < 0) { - tst_brkm(TCONF, NULL, - "This test can only run on kernels that are 3.15. and higher"); - } - tst_tmpdir(); fs_type = tst_fs_type(cleanup, "."); diff --git a/testcases/kernel/syscalls/request_key/.gitignore b/testcases/kernel/syscalls/request_key/.gitignore index e8dc1c57..6dcf613c 100755 --- a/testcases/kernel/syscalls/request_key/.gitignore +++ b/testcases/kernel/syscalls/request_key/.gitignore @@ -3,3 +3,4 @@ /request_key03 /request_key04 /request_key05 +/request_key06 diff --git a/testcases/kernel/syscalls/request_key/request_key01.c b/testcases/kernel/syscalls/request_key/request_key01.c index 251534ef..97121533 100755 --- a/testcases/kernel/syscalls/request_key/request_key01.c +++ b/testcases/kernel/syscalls/request_key/request_key01.c @@ -2,23 +2,18 @@ /* * Copyright (c) 2016 Fujitsu Ltd. * Copyright (c) 2017 Petr Vorel - * + * Copyright (c) Linux Test Project, 2017-2024 * Author: Xiao Yang */ -/* - * Test Name: request_key01 +/*\ + * Test basic functionality of the request_key(2). * - * Description: - * The testcase checks basic functionality of the request_key(2). * request_key(2) asks the kernel to find a key which matches the * specified description. If successful, it attaches it to the * nominated keyring and returns its serial number. - * */ -#include - #include "tst_test.h" #include "lapi/keyctl.h" @@ -27,11 +22,10 @@ static int key; static void verify_request_key(void) { - TEST(request_key("keyring", "ltp", NULL, KEY_REQKEY_DEFL_DEFAULT)); - if (TST_RET == -1) { - tst_res(TFAIL | TTERRNO, "request_key() failed"); + TST_EXP_POSITIVE(request_key("keyring", "ltp", NULL, + KEY_REQKEY_DEFL_DEFAULT)); + if (!TST_PASS) return; - } if (TST_RET != key) tst_res(TFAIL, "serial number mismatched"); diff --git a/testcases/kernel/syscalls/request_key/request_key02.c b/testcases/kernel/syscalls/request_key/request_key02.c index 89a78142..2d35acee 100755 --- a/testcases/kernel/syscalls/request_key/request_key02.c +++ b/testcases/kernel/syscalls/request_key/request_key02.c @@ -2,22 +2,17 @@ /* * Copyright (c) 2016 Fujitsu Ltd. * Copyright (c) 2017 Petr Vorel - * + * Copyright (c) Linux Test Project, 2017-2024 * Author: Xiao Yang */ -/* - * Test Name: request_key02 +/*\ + * Basic request_key(2) failure checking. request_key(2) should return -1 and + * set expected errno: * - * Description: - * 1) request_key(2) fails if no matching key was found. - * 2) request_key(2) fails if A revoked key was found. - * 3) request_key(2) fails if An expired key was found. - * - * Expected Result: - * 1) request_key(2) should return -1 and set errno to ENOKEY. - * 2) request_key(2) should return -1 and set errno to EKEYREVOKED. - * 3) request_key(2) should return -1 and set errno to EKEYEXPIRED. + * 1. ENOKEY (no matching key was found), + * 2. EKEYREVOKED (revoked key was found) + * 3. EKEYEXPIRED (expired key was found) */ #include @@ -43,19 +38,9 @@ static void verify_request_key(unsigned int n) { struct test_case *tc = tcases + n; - TEST(request_key("keyring", tc->des, NULL, *tc->id)); - if (TST_RET != -1) { - tst_res(TFAIL, "request_key() succeed unexpectly"); - return; - } - - if (TST_ERR == tc->exp_err) { - tst_res(TPASS | TTERRNO, "request_key() failed expectly"); - return; - } - - tst_res(TFAIL | TTERRNO, "request_key() failed unexpectly, " - "expected %s", tst_strerrno(tc->exp_err)); + TST_EXP_FAIL2(request_key("keyring", tc->des, NULL, *tc->id), + tc->exp_err, "request_key(\"keyring\", %s, NULL, %d)", + tc->des, *tc->id); } static int init_key(char *name, int cmd) @@ -68,9 +53,8 @@ static int init_key(char *name, int cmd) tst_brk(TBROK | TERRNO, "add_key() failed"); if (cmd == KEYCTL_REVOKE) { - if (keyctl(cmd, n) == -1) { + if (keyctl(cmd, n) == -1) tst_brk(TBROK | TERRNO, "failed to revoke a key"); - } } if (cmd == KEYCTL_SET_TIMEOUT) { diff --git a/testcases/kernel/syscalls/request_key/request_key03.c b/testcases/kernel/syscalls/request_key/request_key03.c index 464fcd8a..bacfbe98 100755 --- a/testcases/kernel/syscalls/request_key/request_key03.c +++ b/testcases/kernel/syscalls/request_key/request_key03.c @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Google, Inc. + * Copyright (c) Linux Test Project, 2018-2024 */ -/* +/*\ * Regression test for two related bugs: * - * (1) CVE-2017-15299, fixed by commit 60ff5b2f547a ("KEYS: don't let add_key() - * update an uninstantiated key") - * (2) CVE-2017-15951, fixed by commit 363b02dab09b ("KEYS: Fix race between - * updating and finding a negative key") + * 1. CVE-2017-15299, fixed by commit 60ff5b2f547a ("KEYS: don't let add_key() + * update an uninstantiated key") + * 2. CVE-2017-15951, fixed by commit 363b02dab09b ("KEYS: Fix race between + * updating and finding a negative key") * * We test for the bugs together because the reproduction steps are essentially * the same: repeatedly try to add/update a key with add_key() while requesting @@ -212,7 +213,7 @@ static struct tst_test test = { .test = do_test, .tcnt = ARRAY_SIZE(testcase_list), .forks_child = 1, - .max_runtime = 20, + .runtime = 20, .options = (struct tst_option[]) { {"b:", &opt_bug, "Bug to test for (cve-2017-15299 or cve-2017-15951; default is both)"}, {} diff --git a/testcases/kernel/syscalls/request_key/request_key04.c b/testcases/kernel/syscalls/request_key/request_key04.c index c125f426..404233ad 100755 --- a/testcases/kernel/syscalls/request_key/request_key04.c +++ b/testcases/kernel/syscalls/request_key/request_key04.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 Google, Inc. + * Copyright (c) Linux Test Project, 2018-2024 */ -/* +/*\ * Regression test for commit 4dca6ea1d943 ("KEYS: add missing permission check * for request_key() destination"), or CVE-2017-17807. This bug allowed adding * a key to a keyring given only Search permission to that keyring, rather than diff --git a/testcases/kernel/syscalls/request_key/request_key05.c b/testcases/kernel/syscalls/request_key/request_key05.c index a17d1d05..8c20bab3 100755 --- a/testcases/kernel/syscalls/request_key/request_key05.c +++ b/testcases/kernel/syscalls/request_key/request_key05.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017 Richard Palethorpe + * Copyright (c) Linux Test Project, 2018-2024 */ -/* - * Test for CVE-2017-6951, original reproducer can be found here: + +/*\ + * Test for CVE-2017-6951, original reproducer: * http://www.spinics.net/lists/keyrings/msg01845.html * * request_key() is not in glibc, so we just use the syscall directly instead diff --git a/testcases/kernel/syscalls/request_key/request_key06.c b/testcases/kernel/syscalls/request_key/request_key06.c new file mode 100644 index 00000000..34cc31fa --- /dev/null +++ b/testcases/kernel/syscalls/request_key/request_key06.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Author: Ma Xinjian + */ + +/*\ + * Verify that request_key(2) fails with + * + * - EFAULT when type points outside the process's accessible address space + * - EFAULT when description points outside the process's accessible address space + * - EFAULT when callout_info points outside the process's accessible address space + * - EPERM when type argument started with a period '.' + */ + +#include "tst_test.h" +#include "lapi/keyctl.h" + +static struct test_case_t { + char *type; + char *description; + char *callout_info; + key_serial_t dest_keyring; + int expected_errno; + char *desc; +} tcases[] = { + {(char *)(-1), "description", NULL, KEY_SPEC_PROCESS_KEYRING, EFAULT, + "type points outside the process's accessible address space"}, + {"type", (char *)(-1), NULL, KEY_SPEC_PROCESS_KEYRING, EFAULT, + "description points outside the process's accessible address space"}, + {"type", "description", (char *)(-1), KEY_SPEC_PROCESS_KEYRING, EFAULT, + "callout_info points outside the process's accessible address space"}, + {".type", "description", NULL, KEY_SPEC_PROCESS_KEYRING, EPERM, + "type argument started with a period '.'"}, +}; + +static void verify_request_key(unsigned int i) +{ + struct test_case_t *tc = &tcases[i]; + + TST_EXP_FAIL2(request_key(tc->type, tc->description, tc->callout_info, + tc->dest_keyring), + tc->expected_errno, + "%s", tc->desc); +} + +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .test = verify_request_key, +}; diff --git a/testcases/kernel/syscalls/rmdir/rmdir02.c b/testcases/kernel/syscalls/rmdir/rmdir02.c index cb0aec85..308f8644 100755 --- a/testcases/kernel/syscalls/rmdir/rmdir02.c +++ b/testcases/kernel/syscalls/rmdir/rmdir02.c @@ -2,17 +2,19 @@ /* Copyright (c) International Business Machines Corp., 2001 * Ported to LTP: Wayne Boyer */ -/* - * Description: - * 1) attempt to rmdir() non-empty directory -> ENOTEMPTY - * 2) attempt to rmdir() directory with long path name -> ENAMETOOLONG - * 3) attempt to rmdir() non-existing directory -> ENOENT - * 4) attempt to rmdir() a file -> ENOTDIR - * 5) attempt to rmdir() invalid pointer -> EFAULT - * 6) attempt to rmdir() symlink loop -> ELOOP - * 7) attempt to rmdir() dir on RO mounted FS -> EROFS - * 8) attempt to rmdir() mount point -> EBUSY - * 9) attempt to rmdir() current directory "." -> EINVAL + +/*\ + * Verify that, rmdir(2) returns -1 and sets errno to + * + * 1. ENOTEMPTY when removing a non-empty directory + * 2. ENAMETOOLONG when removing a directory with long path name + * 3. ENOENT when removing a non-existing directory + * 4. ENOTDIR when removing a a file + * 5. EFAULT when removing a invalid pointer + * 6. ELOOP when removing a symlink loop + * 7. EROFS when removing a dir on RO mounted FS + * 8. EBUSY when removing a mount point + * 9. EINVAL when removing "." (current directory) */ #include diff --git a/testcases/kernel/syscalls/rt_sigqueueinfo/.gitignore b/testcases/kernel/syscalls/rt_sigqueueinfo/.gitignore index 37cd2062..a26811b0 100755 --- a/testcases/kernel/syscalls/rt_sigqueueinfo/.gitignore +++ b/testcases/kernel/syscalls/rt_sigqueueinfo/.gitignore @@ -1 +1,2 @@ /rt_sigqueueinfo01 +/rt_sigqueueinfo02 diff --git a/testcases/kernel/syscalls/rt_sigqueueinfo/rt_sigqueueinfo02.c b/testcases/kernel/syscalls/rt_sigqueueinfo/rt_sigqueueinfo02.c new file mode 100644 index 00000000..ee12bbb8 --- /dev/null +++ b/testcases/kernel/syscalls/rt_sigqueueinfo/rt_sigqueueinfo02.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Author: Ma Xinjian + */ + +/*\ + * Verify that, rt_sigqueueinfo(2) sets errno to + * + * - EINVAL if sig is invalid + * - EPERM if uinfo->si_code is invalid + * - ESRCH if no thread group matching tgid is found + */ + +#include +#include +#include "config.h" +#include "tst_test.h" + +#ifdef HAVE_STRUCT_SIGACTION_SA_SIGACTION +#include "rt_sigqueueinfo.h" + +static siginfo_t siginfo_einval; +static siginfo_t siginfo_eperm; +static siginfo_t siginfo_esrch; + +static pid_t tgid_notfound = -1; + +static struct test_case_t { + pid_t *tgid; + int sig; + siginfo_t *uinfo; + int expected_errno; + char *desc; +} tcases[] = { + {NULL, -1, &siginfo_einval, EINVAL, "sig is invalid"}, + {NULL, SIGUSR1, &siginfo_eperm, EPERM, "uinfo->si_code is invalid"}, + {&tgid_notfound, SIGUSR1, &siginfo_esrch, ESRCH, + "no thread group matching tgid is found"}, +}; + +static void setup(void) +{ + siginfo_einval.si_code = SI_QUEUE; + siginfo_eperm.si_code = 0; + siginfo_esrch.si_code = SI_QUEUE; +} + +static void parent_do(struct test_case_t *tc, pid_t pid) +{ + pid_t real_pid; + + if (tc->tgid) + real_pid = *(tc->tgid); + else + real_pid = pid; + + TST_EXP_FAIL(sys_rt_sigqueueinfo(real_pid, tc->sig, tc->uinfo), + tc->expected_errno, "%s", tc->desc); + TST_CHECKPOINT_WAKE(0); +} + +static void child_do(void) +{ + TST_CHECKPOINT_WAIT(0); +} + +static void verify_rt_sigqueueinfo(unsigned int i) +{ + struct test_case_t *tc = &tcases[i]; + pid_t pid = SAFE_FORK(); + + if (!pid) { + child_do(); + exit(0); + } + parent_do(tc, pid); + SAFE_WAITPID(pid, NULL, 0); +} + +static struct tst_test test = { + .setup = setup, + .tcnt = ARRAY_SIZE(tcases), + .test = verify_rt_sigqueueinfo, + .forks_child = 1, + .needs_checkpoints = 1, + .bufs = (struct tst_buffers []) { + {&siginfo_einval, .size = sizeof(siginfo_einval)}, + {&siginfo_eperm, .size = sizeof(siginfo_eperm)}, + {&siginfo_esrch, .size = sizeof(siginfo_esrch)}, + {}, + } +}; + +#else + TST_TEST_TCONF("This system does not support rt_sigqueueinfo()"); +#endif /* HAVE_STRUCT_SIGACTION_SA_SIGACTION */ diff --git a/testcases/kernel/syscalls/rt_sigtimedwait/Makefile b/testcases/kernel/syscalls/rt_sigtimedwait/Makefile index 1ae50b32..f96d5dc3 100755 --- a/testcases/kernel/syscalls/rt_sigtimedwait/Makefile +++ b/testcases/kernel/syscalls/rt_sigtimedwait/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpsigwait +LTPLIBS = sigwait include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/rt_tgsigqueueinfo/rt_tgsigqueueinfo01.c b/testcases/kernel/syscalls/rt_tgsigqueueinfo/rt_tgsigqueueinfo01.c index bee6a627..9e45f35f 100755 --- a/testcases/kernel/syscalls/rt_tgsigqueueinfo/rt_tgsigqueueinfo01.c +++ b/testcases/kernel/syscalls/rt_tgsigqueueinfo/rt_tgsigqueueinfo01.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 Linaro Limited. All rights reserved. + * Copyright (c) Linux Test Project, 2019-2024 * Author: Sumit Garg */ -/* - * Test rt_tgsigqueueinfo - * - * This tests the rt_tgsigqueueinfo() syscall. It sends the signal and data +/*\ + * Basic test for rt_tgsigqueueinfo(2) syscall. It sends the signal and data * to the single thread specified by the combination of tgid, a thread group * ID, and tid, a thread in that thread group. * * Also this implement 3 tests differing on the basis of signal sender: + * * - Sender and receiver is the same thread. * - Sender is parent of the thread. * - Sender is different thread. @@ -25,12 +25,6 @@ #include "tst_test.h" #include "lapi/syscalls.h" -#ifndef __ANDROID__ -#define SI_SIGVAL si_sigval -#else -#define SI_SIGVAL _sigval -#endif - static char sigval_send[] = "rt_tgsigqueueinfo data"; static volatile int signum_rcv; static char *sigval_rcv; @@ -39,7 +33,7 @@ static void sigusr1_handler(int signum, siginfo_t *uinfo, void *p LTP_ATTRIBUTE_UNUSED) { signum_rcv = signum; - sigval_rcv = uinfo->_sifields._rt.SI_SIGVAL.sival_ptr; + sigval_rcv = uinfo->si_ptr; } void *send_rcv_func(void *arg) @@ -51,7 +45,7 @@ void *send_rcv_func(void *arg) uinfo.si_errno = 0; uinfo.si_code = SI_QUEUE; - uinfo._sifields._rt.SI_SIGVAL.sival_ptr = sigval_send; + uinfo.si_ptr = sigval_send; TEST(tst_syscall(__NR_rt_tgsigqueueinfo, getpid(), syscall(__NR_gettid), SIGUSR1, &uinfo)); @@ -113,7 +107,7 @@ static void verify_signal_parent_thread(void) uinfo.si_errno = 0; uinfo.si_code = SI_QUEUE; - uinfo._sifields._rt.SI_SIGVAL.sival_ptr = sigval_send; + uinfo.si_ptr = sigval_send; TEST(tst_syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, SIGUSR1, &uinfo)); @@ -130,7 +124,7 @@ void *sender_func(void *arg) uinfo.si_errno = 0; uinfo.si_code = SI_QUEUE; - uinfo._sifields._rt.SI_SIGVAL.sival_ptr = sigval_send; + uinfo.si_ptr = sigval_send; TEST(tst_syscall(__NR_rt_tgsigqueueinfo, getpid(), *tid, SIGUSR1, &uinfo)); diff --git a/testcases/kernel/syscalls/sbrk/sbrk01.c b/testcases/kernel/syscalls/sbrk/sbrk01.c index c99fb010..b6a34d0f 100755 --- a/testcases/kernel/syscalls/sbrk/sbrk01.c +++ b/testcases/kernel/syscalls/sbrk/sbrk01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * AUTHOR : William Roske, CO-PILOT : Dave Fenner @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that sbrk() successfully increments or decrements the program's * data break. */ @@ -26,12 +24,8 @@ static void run(unsigned int i) { struct tcase *tc = &tcases[i]; - TESTPTR(sbrk(tc->increment)); - - if (TST_RET_PTR == (void *) -1) - tst_res(TFAIL | TTERRNO, "sbrk(%ld) failed", tc->increment); - else - tst_res(TPASS, "sbrk(%ld) returned %p", tc->increment, TST_RET_PTR); + TST_EXP_PASS_PTR_VOID(sbrk(tc->increment), + "sbrk(%ld) returned %p", tc->increment, TST_RET_PTR); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/sbrk/sbrk02.c b/testcases/kernel/syscalls/sbrk/sbrk02.c index 7fccc4d3..7990196e 100755 --- a/testcases/kernel/syscalls/sbrk/sbrk02.c +++ b/testcases/kernel/syscalls/sbrk/sbrk02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Fujitsu Ltd. * Author: Zeng Linggang @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that sbrk() on failure sets errno to ENOMEM. */ @@ -19,21 +17,8 @@ static long increment = INC; static void run(void) { - TESTPTR(sbrk(increment)); - - if (TST_RET_PTR != (void *)-1) { - tst_res(TFAIL, "sbrk(%ld) unexpectedly passed and returned %p, " - "expected (void *)-1 with errno=%d", - increment, TST_RET_PTR, ENOMEM); - return; - } - - if (TST_ERR == ENOMEM) - tst_res(TPASS | TTERRNO, "sbrk(%ld) failed as expected", increment); - else - tst_res(TFAIL | TTERRNO, "sbrk(%ld) failed but unexpected errno, " - "expected errno=%d - %s", - increment, ENOMEM, strerror(ENOMEM)); + TST_EXP_FAIL_PTR_VOID(sbrk(increment), ENOMEM, + "sbrk(%ld) returned %p", increment, TST_RET_PTR); } static void setup(void) diff --git a/testcases/kernel/syscalls/sbrk/sbrk03.c b/testcases/kernel/syscalls/sbrk/sbrk03.c index 80d2020f..5c22a399 100755 --- a/testcases/kernel/syscalls/sbrk/sbrk03.c +++ b/testcases/kernel/syscalls/sbrk/sbrk03.c @@ -1,14 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2016 Linux Test Project. + * Copyright (c) Linux Test Project, 2016-2024 */ -/* - * DESCRIPTION - * +/*\ * Total s390 2^31 addr space is 0x80000000. * - * 0x80000000 - 0x10000000 = 0x70000000 + * 0x80000000 - 0x10000000 = 0x70000000 * * 0x70000000 is a valid positive intptr_t and adding it to the current offset * produces a valid uintptr_t without overflow (since the MSB being set is OK), @@ -24,12 +22,10 @@ * in glibc, but it should be the kernel since one could call the syscall * directly without using the glibc entry points. * - * The kernel part was fixed on v3.15 by commits: - * 473a06572fcd (s390/compat: convert system call wrappers to C part 02) - * - * Note: - * The reproducer should be built(gcc -m31) in 32bit on s390 platform + * The kernel part was fixed on v3.15 by commit: + * 473a06572fcd ("s390/compat: convert system call wrappers to C part 02") * + * NOTE: The reproducer should be built in 32bit (gcc -m31) on s390 platform. */ #include @@ -39,7 +35,6 @@ static void sbrk_test(void) { -#if defined(__s390__) && defined(TST_ABI32) void *ret1, *ret2; /* set bkr to 0x10000000 */ @@ -59,13 +54,15 @@ static void sbrk_test(void) } tst_res(TPASS, "sbrk verify: %p", ret2); -#else - tst_res(TCONF, "Only works in 32bit on s390 series system"); -#endif } static struct tst_test test = { .test_all = sbrk_test, + .supported_archs = (const char *const []) { + "s390", + NULL + }, + .needs_abi_bits = 32, .tags = (const struct tst_tag[]) { {"linux-git", "473a06572fcd"}, {} diff --git a/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max01.c b/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max01.c index 9d0ebc28..c0b2f5ea 100755 --- a/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max01.c +++ b/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for the sched_get_priority_max(2) system call. * * Obtain different maximum priority for different schedulling policies and diff --git a/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max02.c b/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max02.c index bf1db294..d3a93a59 100755 --- a/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max02.c +++ b/testcases/kernel/syscalls/sched_get_priority_max/sched_get_priority_max02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that given an invalid scheduling policy, sched_get_priority_max(2) * returns -1 with errno EINVAL. */ diff --git a/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min01.c b/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min01.c index 05cb9d1d..26e16a94 100755 --- a/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min01.c +++ b/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for the sched_get_priority_min(2) system call. * * Obtain different minimum priority for different schedulling policies and diff --git a/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min02.c b/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min02.c index 57e49ffd..077beb7f 100755 --- a/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min02.c +++ b/testcases/kernel/syscalls/sched_get_priority_min/sched_get_priority_min02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that given an invalid scheduling policy, sched_get_priority_min(2) * returns -1 with errno EINVAL. */ diff --git a/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c b/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c index c1715d8a..2e06d180 100755 --- a/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c +++ b/testcases/kernel/syscalls/sched_getattr/sched_getattr01.c @@ -96,9 +96,6 @@ int main(int argc, char **argv) tst_require_root(); - if ((tst_kvercmp(3, 14, 0)) < 0) - tst_brkm(TCONF, NULL, "EDF needs kernel 3.14 or higher"); - for (lc = 0; TEST_LOOPING(lc); lc++) { pthread_create(&thread, NULL, run_deadline, NULL); pthread_join(thread, NULL); diff --git a/testcases/kernel/syscalls/sched_getattr/sched_getattr02.c b/testcases/kernel/syscalls/sched_getattr/sched_getattr02.c index 9f4a09f8..e2c0bf43 100755 --- a/testcases/kernel/syscalls/sched_getattr/sched_getattr02.c +++ b/testcases/kernel/syscalls/sched_getattr/sched_getattr02.c @@ -1,108 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Huawei Technologies Co., Ltd., 2015 - * 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. + * Copyright (c) Linux Test Project, 2015-2024 */ - /* Description: - * Verify that: - * 1) sched_getattr fails with unused pid - * 2) sched_getattr fails with invalid address - * 3) sched_getattr fails with invalid value - * 4) sched_getattr fails with invalid flag +/*\ + * Verify that, sched_getattr(2) returns -1 and sets errno to: + * + * 1. ESRCH if pid is unused. + * 2. EINVAL if address is NULL. + * 3. EINVAL if size is invalid. + * 4. EINVAL if flag is not zero. */ #define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include - -#include "test.h" +#include "tst_test.h" #include "lapi/sched.h" -char *TCID = "sched_getattr02"; - static pid_t pid; static pid_t unused_pid; -struct sched_attr attr_copy; +static struct sched_attr attr_copy; static struct test_case { pid_t *pid; - struct sched_attr *a; + struct sched_attr *attr; unsigned int size; unsigned int flags; int exp_errno; -} test_cases[] = { +} tcase[] = { {&unused_pid, &attr_copy, sizeof(struct sched_attr), 0, ESRCH}, {&pid, NULL, sizeof(struct sched_attr), 0, EINVAL}, - {&pid, &attr_copy, sizeof(struct sched_attr) - 1, 0, EINVAL}, + {&pid, &attr_copy, SCHED_ATTR_SIZE_VER0 - 1, 0, EINVAL}, {&pid, &attr_copy, sizeof(struct sched_attr), 1000, EINVAL} }; -static void setup(void); -static void sched_getattr_verify(const struct test_case *test); - -int TST_TOTAL = ARRAY_SIZE(test_cases); - -static void sched_getattr_verify(const struct test_case *test) +static void verify_sched_getattr(unsigned int n) { - TEST(sched_getattr(*(test->pid), test->a, test->size, - test->flags)); + struct test_case *tc = &tcase[n]; - if (TEST_RETURN != -1) { - tst_resm(TFAIL, "sched_getattr() succeeded unexpectedly."); - return; - } - - if (TEST_ERRNO == test->exp_errno) { - tst_resm(TPASS | TTERRNO, - "sched_getattr() failed expectedly"); - return; - } - - tst_resm(TFAIL | TTERRNO, "sched_getattr() failed unexpectedly " - ": expected: %d - %s", - test->exp_errno, tst_strerrno(test->exp_errno)); + TST_EXP_FAIL(sched_getattr(*(tc->pid), tc->attr, tc->size, tc->flags), + tc->exp_errno, "sched_getattr(%d, ..., %d, %d)", *tc->pid, + tc->size, tc->flags); } -int main(int argc, char **argv) +static void setup(void) { - int lc, i; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - for (i = 0; i < TST_TOTAL; i++) - sched_getattr_verify(&test_cases[i]); - } - - tst_exit(); + unused_pid = tst_get_unused_pid(); } -void setup(void) -{ - unused_pid = tst_get_unused_pid(setup); - - if ((tst_kvercmp(3, 14, 0)) < 0) - tst_brkm(TCONF, NULL, "EDF needs kernel 3.14 or higher"); - - TEST_PAUSE; -} +static struct tst_test test = { + .needs_tmpdir = 1, + .test = verify_sched_getattr, + .tcnt = ARRAY_SIZE(tcase), + .setup = setup, +}; diff --git a/testcases/kernel/syscalls/sched_getparam/sched_getparam01.c b/testcases/kernel/syscalls/sched_getparam/sched_getparam01.c index a3572648..554ef2ff 100755 --- a/testcases/kernel/syscalls/sched_getparam/sched_getparam01.c +++ b/testcases/kernel/syscalls/sched_getparam/sched_getparam01.c @@ -4,17 +4,15 @@ */ /*\ - * [Description] - * * Verify that: * * sched_getparam(2) gets correct scheduling parameters for * the specified process: * * - If pid is zero, sched_getparam(2) gets the scheduling parameters - * for the calling process. + * for the calling process. * - If pid is not zero, sched_getparam(2) gets the scheduling - * parameters for the specified [pid] process. + * parameters for the specified [pid] process. */ #include diff --git a/testcases/kernel/syscalls/sched_getparam/sched_getparam03.c b/testcases/kernel/syscalls/sched_getparam/sched_getparam03.c index f1c635ed..567bc6f7 100755 --- a/testcases/kernel/syscalls/sched_getparam/sched_getparam03.c +++ b/testcases/kernel/syscalls/sched_getparam/sched_getparam03.c @@ -4,16 +4,11 @@ */ /*\ - * [Description] + * Verify that, sched_getparam(2) returns -1 and sets errno to * - * Verify that: - * - * - sched_getparam(2) returns -1 and sets errno to ESRCH if the - * process with specified pid could not be found - * - sched_getparam(2) returns -1 and sets errno to EINVAL if - * the parameter pid is an invalid value (-1) - * - sched_getparam(2) returns -1 and sets errno to EINVAL if the - * parameter p is an invalid address + * - ESRCH if the process with specified pid could not be found + * - EINVAL if the parameter pid is an invalid value (-1) + * - EINVAL if the parameter p is an invalid address */ #include diff --git a/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler01.c b/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler01.c index 4191f09c..58bbede9 100755 --- a/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler01.c +++ b/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Testcase to check sched_getscheduler() returns correct return value. * * [Algorithm] @@ -41,6 +39,8 @@ static void run(unsigned int n) struct test_cases_t *tc = &tcases[n]; struct sched_param p = { .sched_priority = tc->priority }; + tst_check_rt_group_sched_support(); + TST_EXP_PASS_SILENT(tv->sched_setscheduler(0, tc->policy, &p)); if (!TST_PASS) diff --git a/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler02.c b/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler02.c index 98818ff4..fbd5d66e 100755 --- a/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler02.c +++ b/testcases/kernel/syscalls/sched_getscheduler/sched_getscheduler02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Pass an unused pid to sched_getscheduler() and test for ESRCH. */ diff --git a/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval01.c b/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval01.c index 597de466..6dbf5af8 100755 --- a/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval01.c +++ b/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval01.c @@ -2,13 +2,15 @@ /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * AUTHOR : Saji Kumar.V.R - * + */ +/*\ * Gets round-robin time quantum by calling sched_rr_get_interval() and * checks that the value is sane. * - * It is also a regression test for kernel - * commit 975e155ed873 ("sched/rt: Show the 'sched_rr_timeslice' SCHED_RR - * timeslice tuning knob in milliseconds"). + * It is also a regression test for: + * + * - 975e155ed873 (sched/rt: Show the 'sched_rr_timeslice' SCHED_RR timeslice tuning knob in milliseconds) + * - c7fcb99877f9 ( sched/rt: Fix sysctl_sched_rr_timeslice intial value) */ #include "time64_variants.h" @@ -41,6 +43,8 @@ static void setup(void) tp.type = tv->ts_type; + tst_check_rt_group_sched_support(); + if ((sys_sched_setscheduler(0, SCHED_RR, &p)) == -1) tst_res(TFAIL | TERRNO, "sched_setscheduler() failed"); diff --git a/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval02.c b/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval02.c index 15e4a305..3f6f34bb 100755 --- a/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval02.c +++ b/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval02.c @@ -2,7 +2,8 @@ /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * AUTHOR : Saji Kumar.V.R - * + */ +/*\ * Verify that for a process with scheduling policy SCHED_FIFO, * sched_rr_get_interval() writes zero into timespec structure * for tv_sec & tv_nsec. @@ -35,6 +36,8 @@ static void setup(void) tp.type = tv->ts_type; + tst_check_rt_group_sched_support(); + if ((sys_sched_setscheduler(0, SCHED_FIFO, &p)) == -1) tst_res(TFAIL | TERRNO, "sched_setscheduler() failed"); } diff --git a/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval03.c b/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval03.c index f5a88f08..d4b7a6f2 100755 --- a/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval03.c +++ b/testcases/kernel/syscalls/sched_rr_get_interval/sched_rr_get_interval03.c @@ -2,14 +2,18 @@ /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * AUTHOR : Saji Kumar.V.R + */ +/*\ + * Verify that: * - * Verify that - * 1) sched_rr_get_interval() fails with errno set to EINVAL for an - * invalid pid - * 2) sched_rr_get_interval() fails with errno set to ESRCH if the - * process with specified pid does not exists - * 3) sched_rr_get_interval() fails with errno set to EFAULT if the - * address specified as &tp is invalid + * - sched_rr_get_interval() fails with errno set to EINVAL for an + * invalid pid + * + * - sched_rr_get_interval() fails with errno set to ESRCH if the + * process with specified pid does not exists + * + * - sched_rr_get_interval() fails with errno set to EFAULT if the + * address specified as &tp is invalid */ #include "time64_variants.h" @@ -55,6 +59,8 @@ static void setup(void) bad_addr = tst_get_bad_addr(NULL); tp.type = tv->ts_type; + tst_check_rt_group_sched_support(); + if ((sys_sched_setscheduler(0, SCHED_RR, &p)) == -1) tst_res(TFAIL | TERRNO, "sched_setscheduler() failed"); diff --git a/testcases/kernel/syscalls/sched_setaffinity/sched_setaffinity01.c b/testcases/kernel/syscalls/sched_setaffinity/sched_setaffinity01.c index a56ae1f5..22fef402 100755 --- a/testcases/kernel/syscalls/sched_setaffinity/sched_setaffinity01.c +++ b/testcases/kernel/syscalls/sched_setaffinity/sched_setaffinity01.c @@ -6,13 +6,13 @@ * Author: Stanislav Kholmanskikh */ -/* - * Description: +/*\ * Check various errnos for sched_setaffinity(): - * 1) EFAULT, if the supplied memory address is invalid. - * 2) EINVAL, if the mask doesn't contain at least one permitted cpu. - * 3) ESRCH, if the process whose id is pid could not be found. - * 4) EPERM, if the calling process doesn't have appropriate privileges. + * + * 1. EFAULT, if the supplied memory address is invalid. + * 2. EINVAL, if the mask doesn't contain at least one permitted cpu. + * 3. ESRCH, if the process whose id is pid could not be found. + * 4. EPERM, if the calling process doesn't have appropriate privileges. */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c b/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c index d5178e01..13380d17 100755 --- a/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c +++ b/testcases/kernel/syscalls/sched_setattr/sched_setattr01.c @@ -130,8 +130,5 @@ void setup(void) tst_require_root(); - if ((tst_kvercmp(3, 14, 0)) < 0) - tst_brkm(TCONF, NULL, "EDF needs kernel 3.14 or higher"); - TEST_PAUSE; } diff --git a/testcases/kernel/syscalls/sched_setparam/sched_setparam01.c b/testcases/kernel/syscalls/sched_setparam/sched_setparam01.c index df789e93..6222b0f8 100755 --- a/testcases/kernel/syscalls/sched_setparam/sched_setparam01.c +++ b/testcases/kernel/syscalls/sched_setparam/sched_setparam01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic test for sched_setparam(2) * * Call sched_setparam(2) with pid=0 so that it will diff --git a/testcases/kernel/syscalls/sched_setparam/sched_setparam02.c b/testcases/kernel/syscalls/sched_setparam/sched_setparam02.c index b71c51c6..99277baf 100755 --- a/testcases/kernel/syscalls/sched_setparam/sched_setparam02.c +++ b/testcases/kernel/syscalls/sched_setparam/sched_setparam02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Checks functionality for sched_setparam(2) * * This test changes the scheduling priority for current process @@ -38,6 +36,8 @@ static void run(unsigned int n) struct sched_variant *tv = &sched_variants[tst_variant]; struct sched_param p = { .sched_priority = tc->param }; + tst_check_rt_group_sched_support(); + TST_EXP_PASS_SILENT(tv->sched_setscheduler(0, tc->policy, &p)); p.sched_priority = tc->priority; diff --git a/testcases/kernel/syscalls/sched_setparam/sched_setparam03.c b/testcases/kernel/syscalls/sched_setparam/sched_setparam03.c index 759b790b..a8effcad 100755 --- a/testcases/kernel/syscalls/sched_setparam/sched_setparam03.c +++ b/testcases/kernel/syscalls/sched_setparam/sched_setparam03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Checks functionality for sched_setparam(2) for pid != 0 * * This test forks a child and changes its parent's scheduling priority. @@ -49,7 +47,7 @@ void setup(void) tst_res(TINFO, "Testing %s variant", tv->desc); if (tv->sched_setscheduler(0, SCHED_FIFO, &p)) - tst_brk(TBROK | TERRNO, "sched_setcheduler(0, SCHED_FIFO, 1)"); + tst_brk(TBROK | TERRNO, "sched_setscheduler(0, SCHED_FIFO, 1)"); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/sched_setparam/sched_setparam04.c b/testcases/kernel/syscalls/sched_setparam/sched_setparam04.c index dbcb3c55..aa47a861 100755 --- a/testcases/kernel/syscalls/sched_setparam/sched_setparam04.c +++ b/testcases/kernel/syscalls/sched_setparam/sched_setparam04.c @@ -2,22 +2,21 @@ /* * Copyright (c) 2021, BELLSOFT. All rights reserved. * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. - * AUTHOR: Saji Kumar.V.R + * Author: Saji Kumar.V.R */ /*\ - * [Description] - * * Verify that: + * * 1. sched_setparam(2) returns -1 and sets errno to ESRCH if the - * process with specified pid could not be found + * process with specified pid could not be found. * 2. sched_setparam(2) returns -1 and sets errno to EINVAL if - * the parameter pid is an invalid value (-1) + * the parameter pid is an invalid value (-1). * 3. sched_setparam(2) returns -1 and sets errno to EINVAL if the - * parameter p is an invalid address + * parameter p is an invalid address. * 4. sched_setparam(2) returns -1 sets errno to EINVAL if the * value for p.sched_priority is other than 0 for scheduling - * policy, SCHED_OTHER + * policy, SCHED_OTHER. */ #include "tst_test.h" diff --git a/testcases/kernel/syscalls/sched_setparam/sched_setparam05.c b/testcases/kernel/syscalls/sched_setparam/sched_setparam05.c index 0a49ecc4..e5fc8b04 100755 --- a/testcases/kernel/syscalls/sched_setparam/sched_setparam05.c +++ b/testcases/kernel/syscalls/sched_setparam/sched_setparam05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that sched_setparam() fails if the user does not have proper * privileges. */ diff --git a/testcases/kernel/syscalls/sched_setscheduler/.gitignore b/testcases/kernel/syscalls/sched_setscheduler/.gitignore index aa8ad969..1b8860d2 100755 --- a/testcases/kernel/syscalls/sched_setscheduler/.gitignore +++ b/testcases/kernel/syscalls/sched_setscheduler/.gitignore @@ -1,3 +1,4 @@ /sched_setscheduler01 /sched_setscheduler02 /sched_setscheduler03 +/sched_setscheduler04 diff --git a/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler01.c b/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler01.c index 4e48de88..32a1a37a 100755 --- a/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler01.c +++ b/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Testcase to test whether sched_setscheduler(2) sets the errnos * correctly. * diff --git a/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler02.c b/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler02.c index 66f7270b..425c0ce2 100755 --- a/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler02.c +++ b/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Testcase to test whether sched_setscheduler(2) sets the errnos * correctly. * diff --git a/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler04.c b/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler04.c new file mode 100644 index 00000000..abdd4c6d --- /dev/null +++ b/testcases/kernel/syscalls/sched_setscheduler/sched_setscheduler04.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Federico Bonfiglio + */ + +/* + * Testcases that test if sched_setscheduler with flag + * SCHED_RESET_ON_FORK restores children policy to + * SCHED_NORMAL. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "tst_test.h" +#include "tst_sched.h" + +struct test_case_t { + int policy; + char *desc; +}; + +static struct test_case_t cases[] = { + { + .policy = SCHED_FIFO, + .desc = "SCHED_FIFO" + }, + { + .policy = SCHED_RR, + .desc = "SCHED_RR" + } +}; + +static void test_reset_on_fork(unsigned int i) +{ + struct sched_variant *tv = &sched_variants[tst_variant]; + struct test_case_t *tc = &cases[i]; + + tst_res(TINFO, "Testing %s variant %s policy", tv->desc, tc->desc); + + struct sched_param param = { .sched_priority = 10 }; + + tv->sched_setscheduler(getpid(), tc->policy | SCHED_RESET_ON_FORK, ¶m); + + pid_t pid = SAFE_FORK(); + + if (pid) { + if (sched_getscheduler(pid) == SCHED_NORMAL) + tst_res(TPASS, "Policy reset to SCHED_NORMAL"); + else + tst_res(TFAIL, "Policy NOT reset to SCHED_NORMAL"); + + sched_getparam(pid, ¶m); + + /* kernel will return sched_priority 0 if task is not RT Policy */ + if (param.sched_priority == 0) + tst_res(TPASS, "Priority set to 0"); + else + tst_res(TFAIL, "Priority not set to 0"); + } +} + +static struct tst_test test = { + .forks_child = 1, + .caps = (struct tst_cap[]) { + TST_CAP(TST_CAP_REQ, CAP_SYS_NICE), + {} + }, + .test_variants = ARRAY_SIZE(sched_variants), + .tcnt = ARRAY_SIZE(cases), + .test = test_reset_on_fork, +}; diff --git a/testcases/kernel/syscalls/seccomp/.gitignore b/testcases/kernel/syscalls/seccomp/.gitignore new file mode 100644 index 00000000..9196906c --- /dev/null +++ b/testcases/kernel/syscalls/seccomp/.gitignore @@ -0,0 +1 @@ +seccomp01 diff --git a/testcases/kernel/syscalls/seccomp/Makefile b/testcases/kernel/syscalls/seccomp/Makefile new file mode 100644 index 00000000..8cf1b902 --- /dev/null +++ b/testcases/kernel/syscalls/seccomp/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/prctl/prctl04.c b/testcases/kernel/syscalls/seccomp/seccomp01.c old mode 100755 new mode 100644 similarity index 72% rename from testcases/kernel/syscalls/prctl/prctl04.c rename to testcases/kernel/syscalls/seccomp/seccomp01.c index 8b135d61..88f2fbef --- a/testcases/kernel/syscalls/prctl/prctl04.c +++ b/testcases/kernel/syscalls/seccomp/seccomp01.c @@ -2,12 +2,12 @@ /* * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. * Author: Yang Xu + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * - * Test PR_GET_SECCOMP and PR_SET_SECCOMP of prctl(2). + * Test PR_GET_SECCOMP and PR_SET_SECCOMP with both prctl(2) and seccomp(2). + * The second one is called via __NR_seccomp using tst_syscall(). * * - If PR_SET_SECCOMP sets the SECCOMP_MODE_STRICT for the calling thread, * the only system call that the thread is permitted to make are read(2), @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include #include #include "tst_test.h" +#include "tst_kconfig.h" #include "lapi/syscalls.h" #include "lapi/prctl.h" #include "config.h" @@ -62,11 +62,11 @@ static const struct sock_fprog strict = { .filter = (struct sock_filter *)strict_filter }; -static void check_strict_mode(int); -static void check_filter_mode(int); +static void check_strict_mode(int mode); +static void check_filter_mode(int mode); static struct tcase { - void (*func_check)(); + void (*func_check)(int mode); int pass_flag; int val; int exp_signal; @@ -94,8 +94,8 @@ static struct tcase { "SECCOMP_MODE_FILTER doesn't permit exit()"} }; - -static int mode_filter_not_supported; +static int strict_not_supported; +static int filter_not_supported; static void check_filter_mode_inherit(void) { @@ -122,13 +122,20 @@ static void check_strict_mode(int val) int fd; char buf[2]; + if (strict_not_supported) + return; + fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0666); - TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT)); - if (TST_RET == -1) { - tst_res(TFAIL | TTERRNO, - "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT failed"); - return; + if (tst_variant == 1) { + TEST(tst_syscall(__NR_seccomp, SECCOMP_SET_MODE_STRICT, 0, NULL)); + if (TST_RET == -1) + tst_brk(TBROK | TERRNO, "seccomp(SECCOMP_SET_MODE_STRICT) error"); + } else { + TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, NULL)); + + if (TST_RET == -1) + tst_brk(TBROK | TERRNO, "prctl(SECCOMP_MODE_STRICT) error"); } switch (val) { @@ -158,18 +165,20 @@ static void check_filter_mode(int val) { int fd; - if (mode_filter_not_supported == 1) { - tst_res(TCONF, "kernel doesn't support SECCOMP_MODE_FILTER"); + if (filter_not_supported) return; - } fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0666); - TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &strict)); - if (TST_RET == -1) { - tst_res(TFAIL | TERRNO, - "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_FILTER failed"); - return; + if (tst_variant == 1) { + TEST(tst_syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, &strict)); + if (TST_RET == -1) + tst_brk(TBROK | TERRNO, "seccomp(SECCOMP_SET_MODE_FILTER) error"); + } else { + TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &strict)); + + if (TST_RET == -1) + tst_brk(TBROK | TERRNO, "prctl(SECCOMP_MODE_FILTER) error"); } switch (val) { @@ -213,39 +222,43 @@ static void verify_prctl(unsigned int n) return; } - if (tc->pass_flag == 2 && mode_filter_not_supported == 0) - tst_res(TFAIL, - "SECCOMP_MODE_FILTER permits exit() unexpectedly"); + if (tc->pass_flag == 2) + tst_res(TFAIL, "SECCOMP_MODE_FILTER permits exit() unexpectedly"); } } static void setup(void) { - TEST(prctl(PR_GET_SECCOMP)); - if (TST_RET == 0) { - tst_res(TINFO, "kernel supports PR_GET/SET_SECCOMP"); + static const char * const kconf_strict[] = {"CONFIG_SECCOMP=y", NULL}; + static const char * const kconf_filter[] = {"CONFIG_SECCOMP_FILTER=y", NULL}; - TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL)); - if (TST_RET == -1 && TST_ERR == EINVAL) { - mode_filter_not_supported = 1; - return; - } + tst_res(TINFO, "Testing variant: %s", + tst_variant == 1 ? "seccomp()" : "pctrl(PR_SET_SECCOMP)"); - tst_res(TINFO, "kernel supports SECCOMP_MODE_FILTER"); - return; + if (tst_kconfig_check(kconf_strict)) { + tst_brk(TCONF, "kernel doesn't support SECCOMP_MODE_STRICT. " + "Skipping CONFIG_SECCOMP tests"); + + strict_not_supported = 1; + } else { + tst_res(TINFO, "kernel supports SECCOMP_MODE_STRICT"); } - if (TST_ERR == EINVAL) - tst_brk(TCONF, "kernel doesn't support PR_GET/SET_SECCOMP"); + if (tst_kconfig_check(kconf_filter)) { + tst_brk(TCONF, "kernel doesn't support SECCOMP_MODE_FILTER. " + "Skipping CONFIG_SECCOMP_FILTER tests"); - tst_brk(TBROK | TTERRNO, - "current environment doesn't permit PR_GET/SET_SECCOMP"); + filter_not_supported = 1; + } else { + tst_res(TINFO, "kernel supports SECCOMP_MODE_FILTER"); + } } static struct tst_test test = { .setup = setup, .test = verify_prctl, .tcnt = ARRAY_SIZE(tcases), + .test_variants = 2, .forks_child = 1, .needs_tmpdir = 1, .needs_root = 1, diff --git a/testcases/kernel/syscalls/select/select01.c b/testcases/kernel/syscalls/select/select01.c index a90aeb89..58a4774d 100755 --- a/testcases/kernel/syscalls/select/select01.c +++ b/testcases/kernel/syscalls/select/select01.c @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* + * Copyright (c) Linux Test Project, 2008-2025 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * http://www.sgi.com + * Authors: Richard Logan, William Roske + */ + +/*\ + * :man2:`select` with no I/O and small timeout to file descriptor of a * - * AUTHOR : Richard Logan - * CO-PILOT : William Roske - * - * 1.) select(2) to fd of regular file with no I/O and small timeout - * 2.) select(2) to fd of system pipe with no I/O and small timeout - * 3.) select(2) of fd of a named-pipe (FIFO) with no I/O and small timeout value + * - regular file + * - system pipe + * - named-pipe (FIFO) */ #include diff --git a/testcases/kernel/syscalls/select/select02.c b/testcases/kernel/syscalls/select/select02.c index 784ec921..541d9367 100755 --- a/testcases/kernel/syscalls/select/select02.c +++ b/testcases/kernel/syscalls/select/select02.c @@ -3,9 +3,10 @@ * Copyright (C) 2015-2017 Cyril Hrubis */ -/* - * Check that select() timeouts correctly. +/*\ + * Check that :man2:`select` timeouts correctly. */ + #include #include #include diff --git a/testcases/kernel/syscalls/select/select03.c b/testcases/kernel/syscalls/select/select03.c index 1cec3a4c..6ad1d051 100755 --- a/testcases/kernel/syscalls/select/select03.c +++ b/testcases/kernel/syscalls/select/select03.c @@ -1,8 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Linaro Ltd. + */ + +/*\ + * :man2:`select` failure tests: * - * Failure tests. + * - negative nfds (EINVAL) + * - invalid readfds (EBADF) + * - invalid writefds (EBADF) + * - invalid exceptfds (EBADF) + * - faulty readfds (EFAULT) + * - faulty writefds (EFAULT) + * - faulty exceptfds (EFAULT) + * - faulty timeout (EFAULT) */ #include diff --git a/testcases/kernel/syscalls/select/select04.c b/testcases/kernel/syscalls/select/select04.c index f7a463ee..87791c92 100755 --- a/testcases/kernel/syscalls/select/select04.c +++ b/testcases/kernel/syscalls/select/select04.c @@ -5,9 +5,7 @@ */ /*\ - * [Description] - * - * Test to check if fd set bits are cleared by select(). + * Test to check if fd set bits are cleared by :man2:`select`. * * [Algorithm] * - Check that writefds flag is cleared on full pipe diff --git a/testcases/kernel/syscalls/send/send01.c b/testcases/kernel/syscalls/send/send01.c index 2e0ae217..41859ff6 100755 --- a/testcases/kernel/syscalls/send/send01.c +++ b/testcases/kernel/syscalls/send/send01.c @@ -100,8 +100,6 @@ static struct test_case_t tdat[] = { .cleanup = cleanup0, .desc = "invalid socket"} , -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ {.domain = PF_INET, .type = SOCK_STREAM, .proto = 0, @@ -114,7 +112,6 @@ static struct test_case_t tdat[] = { .cleanup = cleanup1, .desc = "invalid send buffer"} , -#endif {.domain = PF_INET, .type = SOCK_DGRAM, .proto = 0, @@ -139,8 +136,6 @@ static struct test_case_t tdat[] = { .cleanup = cleanup1, .desc = "local endpoint shutdown"} , -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ {.domain = PF_INET, .type = SOCK_DGRAM, .proto = 0, @@ -152,15 +147,10 @@ static struct test_case_t tdat[] = { .setup = setup1, .cleanup = cleanup1, .desc = "invalid flags set"} -#endif }; int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); -#ifdef UCLINUX -static char *argv0; -#endif - static pid_t start_server(struct sockaddr_in *sin0) { pid_t pid; @@ -185,15 +175,9 @@ static pid_t start_server(struct sockaddr_in *sin0) } SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen); - switch ((pid = FORK_OR_VFORK())) { + switch ((pid = tst_fork())) { case 0: -#ifdef UCLINUX - if (self_exec(argv0, "d", sfd) < 0) - tst_brkm(TBROK | TERRNO, cleanup, - "server self_exec failed"); -#else do_child(); -#endif break; case -1: tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); @@ -253,11 +237,6 @@ int main(int ac, char *av[]) tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - argv0 = av[0]; - maybe_run_child(&do_child, "d", &sfd); -#endif - setup(); for (lc = 0; TEST_LOOPING(lc); ++lc) { diff --git a/testcases/kernel/syscalls/send/send02.c b/testcases/kernel/syscalls/send/send02.c index 4d8da161..25d841bf 100755 --- a/testcases/kernel/syscalls/send/send02.c +++ b/testcases/kernel/syscalls/send/send02.c @@ -3,9 +3,9 @@ * Copyright (c) 2020 SUSE LLC */ -/* +/*\ * Check that the kernel correctly handles send()/sendto()/sendmsg() calls - * with MSG_MORE flag + * with MSG_MORE flag. */ #define _GNU_SOURCE @@ -128,7 +128,7 @@ static void run(unsigned int n) struct test_case *tc = testcase_list + n; socklen_t len = sizeof(addr); - tst_res(TINFO, "Tesing %s", tc->name); + tst_res(TINFO, "Testing %s", tc->name); tst_init_sockaddr_inet_bin(&addr, INADDR_LOOPBACK, 0); listen_sock = SAFE_SOCKET(tc->domain, tc->type, tc->protocol); diff --git a/testcases/kernel/syscalls/sendfile/sendfile02.c b/testcases/kernel/syscalls/sendfile/sendfile02.c index 8e88dec2..c88b93b1 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile02.c +++ b/testcases/kernel/syscalls/sendfile/sendfile02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test the basic functionality of the sendfile() system call: * * 1. Call sendfile() with offset = 0. diff --git a/testcases/kernel/syscalls/sendfile/sendfile03.c b/testcases/kernel/syscalls/sendfile/sendfile03.c index 85a3b0f3..bd6e9a8f 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile03.c +++ b/testcases/kernel/syscalls/sendfile/sendfile03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Testcase to test that sendfile(2) system call returns EBADF when passing * wrong out_fd or in_fd. * diff --git a/testcases/kernel/syscalls/sendfile/sendfile04.c b/testcases/kernel/syscalls/sendfile/sendfile04.c index 4fa74813..f74db177 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile04.c +++ b/testcases/kernel/syscalls/sendfile/sendfile04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Testcase to test that sendfile(2) system call returns EFAULT when passing * wrong offset pointer. * diff --git a/testcases/kernel/syscalls/sendfile/sendfile05.c b/testcases/kernel/syscalls/sendfile/sendfile05.c index 691ed973..865d83c3 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile05.c +++ b/testcases/kernel/syscalls/sendfile/sendfile05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Testcase to test that sendfile(2) system call returns EINVAL when passing * negative offset. * diff --git a/testcases/kernel/syscalls/sendfile/sendfile06.c b/testcases/kernel/syscalls/sendfile/sendfile06.c index 2168da72..9b2d8ff5 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile06.c +++ b/testcases/kernel/syscalls/sendfile/sendfile06.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test that sendfile() system call updates file position of in_fd correctly * when passing NULL as offset. */ diff --git a/testcases/kernel/syscalls/sendfile/sendfile07.c b/testcases/kernel/syscalls/sendfile/sendfile07.c index f9cbb7b8..47d312a3 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile07.c +++ b/testcases/kernel/syscalls/sendfile/sendfile07.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Testcase to test that sendfile(2) system call returns EAGAIN * when passing full out_fd opened with O_NONBLOCK. */ diff --git a/testcases/kernel/syscalls/sendfile/sendfile08.c b/testcases/kernel/syscalls/sendfile/sendfile08.c index 66fd40ca..1efff97c 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile08.c +++ b/testcases/kernel/syscalls/sendfile/sendfile08.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Bug in the splice code has caused the file position on the write side * of the sendfile system call to be incorrectly set to the read side file * position. This can result in the data being written to an incorrect offset. diff --git a/testcases/kernel/syscalls/sendfile/sendfile09.c b/testcases/kernel/syscalls/sendfile/sendfile09.c index 4a2d2083..82feaeba 100755 --- a/testcases/kernel/syscalls/sendfile/sendfile09.c +++ b/testcases/kernel/syscalls/sendfile/sendfile09.c @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2014 + * Copyright (c) Linux Test Project, 2013-2023 */ /*\ - * [Description] - * * Testcase copied from sendfile02.c to test the basic functionality of * the sendfile() system call on large file. There is a kernel bug which * introduced by commit 8f9c0119d7ba9 and fixed by commit 5d73320a96fcc. @@ -20,11 +19,7 @@ #include #include - #include "tst_test.h" -#include "lapi/abisize.h" - -#ifndef TST_ABI32 #define ONE_GB (INT64_C(1) << 30) #define IN_FILE "in_file" @@ -96,13 +91,10 @@ static struct tst_test test = { .setup = setup, .test = run, .tcnt = ARRAY_SIZE(tc), - .max_runtime = 120, + .timeout = 120, + .skip_in_compat = 1, .tags = (const struct tst_tag[]) { {"linux-git", "5d73320a96fcc"}, {} } }; - -#else -TST_TEST_TCONF("This test is only for 64bit"); -#endif diff --git a/testcases/kernel/syscalls/sendmmsg/sendmmsg.h b/testcases/kernel/syscalls/sendmmsg/sendmmsg.h index 65d5b680..69ed80d4 100755 --- a/testcases/kernel/syscalls/sendmmsg/sendmmsg.h +++ b/testcases/kernel/syscalls/sendmmsg/sendmmsg.h @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef SENDMMSG_H__ +#define SENDMMSG_H__ + #include #include #include @@ -25,3 +28,5 @@ static struct time64_variants variants[] = { { .recvmmsg = sys_recvmmsg64, .sendmmsg = sys_sendmmsg, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"}, #endif }; + +#endif /* SENDMMSG_H__ */ diff --git a/testcases/kernel/syscalls/sendmsg/sendmsg01.c b/testcases/kernel/syscalls/sendmsg/sendmsg01.c index cf6e7428..38cd7182 100755 --- a/testcases/kernel/syscalls/sendmsg/sendmsg01.c +++ b/testcases/kernel/syscalls/sendmsg/sendmsg01.c @@ -358,21 +358,12 @@ struct test_case_t tdat[] = { int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); -#ifdef UCLINUX -static char *argv0; -#endif - int main(int argc, char *argv[]) { int lc; tst_parse_opts(argc, argv, NULL, NULL); -#ifdef UCLINUX - argv0 = argv[0]; - maybe_run_child(&do_child, "dd", &sfd, &ufd); -#endif - setup(); for (lc = 0; TEST_LOOPING(lc); ++lc) { @@ -457,14 +448,9 @@ static pid_t start_server(struct sockaddr_in *sin0, struct sockaddr_un *sun0) return -1; } - switch ((pid = FORK_OR_VFORK())) { + switch ((pid = tst_fork())) { case 0: -#ifdef UCLINUX - if (self_exec(argv0, "dd", sfd, ufd) < 0) - tst_brkm(TBROK, cleanup, "server self_exec failed"); -#else do_child(); -#endif break; case -1: tst_brkm(TBROK, cleanup, "server fork failed: %s", diff --git a/testcases/kernel/syscalls/sendmsg/sendmsg03.c b/testcases/kernel/syscalls/sendmsg/sendmsg03.c index 38459990..34ebc7e9 100755 --- a/testcases/kernel/syscalls/sendmsg/sendmsg03.c +++ b/testcases/kernel/syscalls/sendmsg/sendmsg03.c @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 SUSE LLC - * + */ + +/*\ * CVE-2017-17712 * * Test for race condition vulnerability in sendmsg() on SOCK_RAW sockets. * Changing the value of IP_HDRINCL socket option in parallel with sendmsg() * call may lead to uninitialized stack pointer usage, allowing arbitrary code - * execution or privilege escalation. Fixed in: + * execution or privilege escalation. * - * commit 8f659a03a0ba9289b9aeb9b4470e6fb263d6f483 - * Author: Mohamed Ghannam - * Date: Sun Dec 10 03:50:58 2017 +0000 - * - * net: ipv4: fix for a race condition in raw_sendmsg + * Fixed in 4.15 + * 8f659a03a0ba ("net: ipv4: fix for a race condition in raw_sendmsg") */ + #include #include #include @@ -103,7 +103,8 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .taint_check = TST_TAINT_W | TST_TAINT_D, - .max_runtime = 150, + .runtime = 150, + .min_runtime = 2, .needs_kconfigs = (const char *[]) { "CONFIG_USER_NS=y", "CONFIG_NET_NS=y", diff --git a/testcases/kernel/syscalls/sendto/sendto01.c b/testcases/kernel/syscalls/sendto/sendto01.c index 6fe0274e..b3b7b6ef 100755 --- a/testcases/kernel/syscalls/sendto/sendto01.c +++ b/testcases/kernel/syscalls/sendto/sendto01.c @@ -106,8 +106,6 @@ struct test_case_t tdat[] = { .cleanup = cleanup0, .desc = "invalid socket"} , -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ {.domain = PF_INET, .type = SOCK_DGRAM, .proto = 0, @@ -122,7 +120,6 @@ struct test_case_t tdat[] = { .cleanup = cleanup1, .desc = "invalid send buffer"} , -#endif {.domain = PF_INET, .type = SOCK_STREAM, .proto = 0, @@ -165,8 +162,6 @@ struct test_case_t tdat[] = { .cleanup = cleanup1, .desc = "invalid to buffer length"} , -#ifndef UCLINUX - /* Skip since uClinux does not implement memory protection */ {.domain = PF_INET, .type = SOCK_DGRAM, .proto = 0, @@ -181,7 +176,6 @@ struct test_case_t tdat[] = { .cleanup = cleanup1, .desc = "invalid to buffer"} , -#endif {.domain = PF_INET, .type = SOCK_DGRAM, .proto = 0, @@ -227,10 +221,6 @@ struct test_case_t tdat[] = { int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); -#ifdef UCLINUX -static char *argv0; -#endif - static pid_t start_server(struct sockaddr_in *sin0) { pid_t pid; @@ -255,15 +245,9 @@ static pid_t start_server(struct sockaddr_in *sin0) } SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen); - switch ((pid = FORK_OR_VFORK())) { + switch ((pid = tst_fork())) { case 0: -#ifdef UCLINUX - if (self_exec(argv0, "d", sfd) < 0) - tst_brkm(TBROK | TERRNO, cleanup, - "server self_exec failed"); -#else do_child(); -#endif break; case -1: tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); @@ -323,11 +307,6 @@ int main(int ac, char *av[]) tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - argv0 = av[0]; - maybe_run_child(&do_child, "d", &sfd); -#endif - setup(); for (lc = 0; TEST_LOOPING(lc); ++lc) { diff --git a/testcases/kernel/syscalls/sendto/sendto02.c b/testcases/kernel/syscalls/sendto/sendto02.c index 74310173..0acbb974 100755 --- a/testcases/kernel/syscalls/sendto/sendto02.c +++ b/testcases/kernel/syscalls/sendto/sendto02.c @@ -2,19 +2,16 @@ /* * Copyright(c) 2016 Fujitsu Ltd. * Author: Xiao Yang + * Copyright (c) Linux Test Project, 2017-2019 */ -/* - * Test Name: sendto02 - * - * Description: - * When sctp protocol is selected in socket(2) and buffer is invalid, +/*\ + * When SCTP protocol created wih socket(2) and buffer is invalid, * sendto(2) should fail and set errno to EFAULT, but it sets errno * to ENOMEM. * - * This is a regression test and has been fixed by kernel commit: - * 6e51fe7572590d8d86e93b547fab6693d305fd0d (sctp: fix -ENOMEM result - * with invalid user space pointer in sendto() syscall) + * This is a regression test fixed by kernel 3.7 + * 6e51fe757259 (sctp: fix -ENOMEM result with invalid user space pointer in sendto() syscall) */ #include diff --git a/testcases/kernel/syscalls/sendto/sendto03.c b/testcases/kernel/syscalls/sendto/sendto03.c index b07d5122..622de647 100755 --- a/testcases/kernel/syscalls/sendto/sendto03.c +++ b/testcases/kernel/syscalls/sendto/sendto03.c @@ -3,18 +3,15 @@ * Copyright (c) 2019 SUSE LLC */ -/* +/*\ * CVE-2020-14386 * * Check for vulnerability in tpacket_rcv() which allows an unprivileged user * to write arbitrary data to a memory area outside the allocated packet - * buffer. Kernel crash fixed in: + * buffer. * - * commit acf69c946233259ab4d64f8869d4037a198c7f06 - * Author: Or Cohen - * Date: Thu Sep 3 21:05:28 2020 -0700 - * - * net/packet: fix overflow in tpacket_rcv + * Kernel crash fixed in 5.9 + * acf69c946233 ("net/packet: fix overflow in tpacket_rcv") */ #include diff --git a/testcases/kernel/syscalls/set_mempolicy/Makefile b/testcases/kernel/syscalls/set_mempolicy/Makefile index 100780dc..f203aa21 100755 --- a/testcases/kernel/syscalls/set_mempolicy/Makefile +++ b/testcases/kernel/syscalls/set_mempolicy/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later top_srcdir ?= ../../../.. -LTPLIBS = ltpnuma +LTPLIBS = numa include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/set_mempolicy/set_mempolicy01.c b/testcases/kernel/syscalls/set_mempolicy/set_mempolicy01.c index e9712b7b..39e7156d 100755 --- a/testcases/kernel/syscalls/set_mempolicy/set_mempolicy01.c +++ b/testcases/kernel/syscalls/set_mempolicy/set_mempolicy01.c @@ -41,9 +41,9 @@ static void setup(void) * has multiple NUMA nodes, the test matrix combination grows exponentially * and bring about test time to increase extremely fast. * - * Here reset the maximum runtime according to the NUMA nodes. + * Here reset the entire timeout according to the NUMA nodes. */ - tst_set_max_runtime(test.max_runtime * (1 << nodes->cnt/16)); + tst_set_timeout(test.timeout * (1 << nodes->cnt/16)); } static void cleanup(void) @@ -119,7 +119,7 @@ static struct tst_test test = { .tcnt = 2, .forks_child = 1, .needs_checkpoints = 1, - .max_runtime = 600, + .timeout = 600, }; #else diff --git a/testcases/kernel/syscalls/set_mempolicy/set_mempolicy05.c b/testcases/kernel/syscalls/set_mempolicy/set_mempolicy05.c index 9cc83e64..65061bf3 100755 --- a/testcases/kernel/syscalls/set_mempolicy/set_mempolicy05.c +++ b/testcases/kernel/syscalls/set_mempolicy/set_mempolicy05.c @@ -3,8 +3,6 @@ * Copyright (C) 2021 SUSE LLC */ /*\ - * - * [Description] * * This will reproduce an information leak in the set_mempolicy 32-bit * compat syscall. The catch is that the 32-bit compat syscall is not diff --git a/testcases/kernel/syscalls/set_thread_area/.gitignore b/testcases/kernel/syscalls/set_thread_area/.gitignore index 547eb86e..b80ce741 100755 --- a/testcases/kernel/syscalls/set_thread_area/.gitignore +++ b/testcases/kernel/syscalls/set_thread_area/.gitignore @@ -1 +1,2 @@ -/set_thread_area01 +set_thread_area01 +set_thread_area02 diff --git a/testcases/kernel/syscalls/set_thread_area/set_thread_area.h b/testcases/kernel/syscalls/set_thread_area/set_thread_area.h deleted file mode 100755 index 2bd2469d..00000000 --- a/testcases/kernel/syscalls/set_thread_area/set_thread_area.h +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include - -/* Harness Specific Include Files. */ -#include "test.h" -#include "lapi/syscalls.h" -#include "config.h" - -#if defined HAVE_ASM_LDT_H -#include -#include - -#if defined HAVE_STRUCT_USER_DESC -typedef struct user_desc thread_area_s; -#elif defined HAVE_STRUCT_MODIFY_LDT_LDT_S -typedef struct modify_ldt_ldt_s thread_area_s; -#else -typedef struct user_desc { - unsigned int entry_number; - unsigned long int base_addr; - unsigned int limit; - unsigned int seg_32bit:1; - unsigned int contents:2; - unsigned int read_exec_only:1; - unsigned int limit_in_pages:1; - unsigned int seg_not_present:1; - unsigned int useable:1; - unsigned int empty:25; -} thread_area_s; -#endif /* HAVE_STRUCT_USER_DESC */ -#endif /* HAVE_ASM_LDT_H */ diff --git a/testcases/kernel/syscalls/set_thread_area/set_thread_area01.c b/testcases/kernel/syscalls/set_thread_area/set_thread_area01.c index 30626d5e..3892d42a 100755 --- a/testcases/kernel/syscalls/set_thread_area/set_thread_area01.c +++ b/testcases/kernel/syscalls/set_thread_area/set_thread_area01.c @@ -1,111 +1,60 @@ -/************************************************************************* +// SPDX-License-Identifier: GPL-2.0-or-later +/* * Copyright (c) Crackerjack Project., 2007 * Copyright (c) Manas Kumar Nayak * Copyright (c) Cyril Hrubis 2011 - * - * 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 - * - ************************************************************************/ + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ -#include "set_thread_area.h" +/*\ + * Basic test of i386 thread-local storage for set_thread_area and + * get_thread_area syscalls. It verifies a simple write and read of an entry + * works. + * + * [Algorithm] + * + * - Call set_thread_area to a struct user_desc pointer with entry_number = -1, + * which will be set to a free entry_number upon exiting. + * - Call get_thread_area to read the new entry. + * - Use the new entry_number in another pointer and call get_thread_area. + * - Make sure they have the same data. + */ -char *TCID = "set_thread_area_01"; -int TST_TOTAL = 6; +#include "tst_test.h" -#if defined(HAVE_ASM_LDT_H) && defined(HAVE_STRUCT_USER_DESC) +#include "lapi/ldt.h" -static void cleanup(void) +static struct user_desc *u_info1; +static struct user_desc *u_info2; + +static void run(void) { + TST_EXP_PASS_SILENT(set_thread_area(u_info1)); + TST_EXP_PASS_SILENT(get_thread_area(u_info1)); + + u_info2->entry_number = u_info1->entry_number; + TST_EXP_PASS_SILENT(get_thread_area(u_info2)); + + TST_EXP_PASS(memcmp(u_info1, u_info2, sizeof(struct user_desc))); } static void setup(void) { - TEST_PAUSE; + /* When set_thread_area() is passed an entry_number of -1, it searches + * for a free TLS entry. If set_thread_area() finds a free TLS entry, + * the value of u_info->entry_number is set upon return to show which + * entry was changed. + */ + u_info1->entry_number = -1; } -struct test { - int syscall; - const char *const syscall_name; - thread_area_s *u_info; - int exp_ret; - int exp_errno; +static struct tst_test test = { + .setup = setup, + .test_all = run, + .supported_archs = (const char *const[]){ "x86", NULL }, + .bufs = (struct tst_buffers[]) { + { &u_info1, .size = sizeof(struct user_desc) }, + { &u_info2, .size = sizeof(struct user_desc) }, + {}, + }, }; - -/* - * The set_thread_area uses a free entry_number if entry number is set to -1 - * and upon the syscall exit the entry number is set to entry which was used. - * So when we call get_thread_area on u_info1, the entry number is initalized - * corectly by the previous set_thread_area. - */ -static struct user_desc u_info1 = {.entry_number = -1 }; -static struct user_desc u_info2 = {.entry_number = -2 }; - -#define VALUE_AND_STRING(val) val, #val - -static struct test tests[] = { - {VALUE_AND_STRING(__NR_set_thread_area), &u_info1, 0, 0}, - {VALUE_AND_STRING(__NR_get_thread_area), &u_info1, 0, 0}, - {VALUE_AND_STRING(__NR_set_thread_area), &u_info2, -1, EINVAL}, - {VALUE_AND_STRING(__NR_get_thread_area), &u_info2, -1, EINVAL}, - {VALUE_AND_STRING(__NR_set_thread_area), (void *)-9, -1, EFAULT}, - {VALUE_AND_STRING(__NR_get_thread_area), (void *)-9, -1, EFAULT}, -}; - -int main(int argc, char *argv[]) -{ - int lc; - unsigned i; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - for (i = 0; i < sizeof(tests) / sizeof(struct test); i++) { - TEST(tst_syscall(tests[i].syscall, tests[i].u_info)); - - if (TEST_RETURN != tests[i].exp_ret) { - tst_resm(TFAIL, "%s returned %li expected %i", - tests[i].syscall_name, - TEST_RETURN, tests[i].exp_ret); - continue; - } - - if (TEST_ERRNO != tests[i].exp_errno) { - tst_resm(TFAIL, - "%s failed with %i (%s) expected %i (%s)", - tests[i].syscall_name, TEST_ERRNO, - strerror(TEST_ERRNO), - tests[i].exp_errno, - strerror(tests[i].exp_errno)); - continue; - } - - tst_resm(TPASS, "%s returned %li errno %i (%s)", - tests[i].syscall_name, TEST_RETURN, - TEST_ERRNO, strerror(TEST_ERRNO)); - } - } - - cleanup(); - tst_exit(); -} -#else -int main(void) -{ - tst_brkm(TCONF, NULL, - "set_thread_area isn't available for this architecture"); -} -#endif diff --git a/testcases/kernel/syscalls/set_thread_area/set_thread_area02.c b/testcases/kernel/syscalls/set_thread_area/set_thread_area02.c new file mode 100644 index 00000000..f1997974 --- /dev/null +++ b/testcases/kernel/syscalls/set_thread_area/set_thread_area02.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Crackerjack Project., 2007 + * Copyright (c) Manas Kumar Nayak + * Copyright (c) Cyril Hrubis 2011 + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière + */ + +/*\ + * Tests set_thread_area and get_thread_area syscalls for their expected errors: + * + * - EINVAL u_info->entry_number is out of bounds. + * - EFAULT u_info is an invalid pointer. + */ + +#include "tst_test.h" + +#include "lapi/ldt.h" + +static struct user_desc *u_info; + +static struct tcase { + struct user_desc *u_info; + int exp_errno; +} tcases[] = { + { NULL, EINVAL }, + { (void *)-9, EFAULT }, +}; + +static void run(unsigned int i) +{ + struct tcase tc = tcases[i]; + + if (tst_variant) + TST_EXP_FAIL(get_thread_area(tc.u_info), tc.exp_errno); + else + TST_EXP_FAIL(set_thread_area(tc.u_info), tc.exp_errno); +} + +static void setup(void) +{ + /* This makes *entry invalid */ + u_info->entry_number = -2; + tcases[0].u_info = u_info; +} + +static struct tst_test test = { + .setup = setup, + .tcnt = ARRAY_SIZE(tcases), + .test = run, + .test_variants = 2, + .supported_archs = (const char *const[]){ "x86", NULL }, + .bufs = (struct tst_buffers[]) { + { &u_info, .size = sizeof(struct user_desc) }, + {}, + }, +}; diff --git a/testcases/kernel/syscalls/set_tid_address/set_tid_address01.c b/testcases/kernel/syscalls/set_tid_address/set_tid_address01.c index 11fa2754..1234e7f5 100755 --- a/testcases/kernel/syscalls/set_tid_address/set_tid_address01.c +++ b/testcases/kernel/syscalls/set_tid_address/set_tid_address01.c @@ -1,129 +1,23 @@ -/******************************************************************************/ -/* Copyright (c) Crackerjack Project., 2007 */ -/* */ -/* 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 */ -/* */ -/******************************************************************************/ -/******************************************************************************/ -/* */ -/* File: set_tid_address01.c */ -/* */ -/* Description: This tests the set_tid_address() syscall */ -/* */ -/* Usage: */ -/* set_tid_address01 [-c n] [-e][-i n] [-I x] [-p x] [-t] */ -/* where, -c n : Run n copies concurrently. */ -/* -e : Turn on errno logging. */ -/* -i n : Execute test n times. */ -/* -I x : Execute test for x seconds. */ -/* -P x : Pause for x seconds between iterations. */ -/* -t : Turn on syscall timing. */ -/* */ -/* Total Tests: 1 */ -/* */ -/* Test Name: set_tid_address01 */ -/* History: Porting from Crackerjack to LTP is done by */ -/* Manas Kumar Nayak maknayak@in.ibm.com> */ -/******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (c) Crackerjack Project., 2007 + * Copyright (c) Linux Test Project, 2007-2024 + */ -#include -#include +/*\ + * Verify the basic functionality of set_tid_address() syscall. + */ -#include "test.h" +#include "tst_test.h" #include "lapi/syscalls.h" -char *TCID = "set_tid_address01"; -int testno; -int TST_TOTAL = 1; - -/* Extern Global Functions */ -/******************************************************************************/ -/* */ -/* Function: cleanup */ -/* */ -/* Description: Performs all one time clean up for this test on successful */ -/* completion, premature exit or failure. Closes all temporary */ -/* files, removes all temporary directories exits the test with */ -/* appropriate return code by calling tst_exit() function. */ -/* */ -/* Input: None. */ -/* */ -/* Output: None. */ -/* */ -/* Return: On failure - Exits calling tst_exit(). Non '0' return code. */ -/* On success - Exits calling tst_exit(). With '0' return code. */ -/* */ -/******************************************************************************/ -void cleanup(void) -{ - - tst_rmdir(); - - tst_exit(); -} - -/* Local Functions */ -/******************************************************************************/ -/* */ -/* Function: setup */ -/* */ -/* Description: Performs all one time setup for this test. This function is */ -/* typically used to capture signals, create temporary dirs */ -/* and temporary files that may be used in the course of this */ -/* test. */ -/* */ -/* Input: None. */ -/* */ -/* Output: None. */ -/* */ -/* Return: On failure - Exits by calling cleanup(). */ -/* On success - returns 0. */ -/* */ -/******************************************************************************/ -void setup(void) -{ - /* Capture signals if any */ - /* Create temporary directories */ - TEST_PAUSE; - tst_tmpdir(); -} - -int main(int ac, char **av) +static void verify_set_tid_address(void) { int newtid = -1; - int lc; - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - tst_count = 0; - for (testno = 0; testno < TST_TOTAL; ++testno) { - TEST(tst_syscall(__NR_set_tid_address, &newtid)); - if (TEST_RETURN == getpid()) { - tst_resm(TPASS, - "set_tid_address call succeeded: as expected %ld", - TEST_RETURN); - } else { - tst_brkm(TFAIL, cleanup, "%s failed - errno = %d : %s", - TCID, TEST_ERRNO, - strerror(TEST_ERRNO)); - } - } - } - cleanup(); - tst_exit(); + TST_EXP_VAL(tst_syscall(__NR_set_tid_address, &newtid), getpid()); } + +static struct tst_test test = { + .test_all = verify_set_tid_address, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/setdomainname/setdomainname01.c b/testcases/kernel/syscalls/setdomainname/setdomainname01.c index 4c2c5312..c50f787b 100755 --- a/testcases/kernel/syscalls/setdomainname/setdomainname01.c +++ b/testcases/kernel/syscalls/setdomainname/setdomainname01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Copyright (c) 2019 Petr Vorel diff --git a/testcases/kernel/syscalls/setdomainname/setdomainname02.c b/testcases/kernel/syscalls/setdomainname/setdomainname02.c index 875ed0c4..e710a7bf 100755 --- a/testcases/kernel/syscalls/setdomainname/setdomainname02.c +++ b/testcases/kernel/syscalls/setdomainname/setdomainname02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Copyright (c) 2019 Petr Vorel diff --git a/testcases/kernel/syscalls/setdomainname/setdomainname03.c b/testcases/kernel/syscalls/setdomainname/setdomainname03.c index b8d17d8d..3cd1ce3d 100755 --- a/testcases/kernel/syscalls/setdomainname/setdomainname03.c +++ b/testcases/kernel/syscalls/setdomainname/setdomainname03.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Copyright (c) 2019 Petr Vorel diff --git a/testcases/kernel/syscalls/setegid/setegid01.c b/testcases/kernel/syscalls/setegid/setegid01.c index eef66311..871b4172 100755 --- a/testcases/kernel/syscalls/setegid/setegid01.c +++ b/testcases/kernel/syscalls/setegid/setegid01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that setegid() sets the effective UID of the calling process * correctly, and does not modify the saved GID and real GID. */ diff --git a/testcases/kernel/syscalls/setegid/setegid02.c b/testcases/kernel/syscalls/setegid/setegid02.c index 66a8a07f..825809fc 100755 --- a/testcases/kernel/syscalls/setegid/setegid02.c +++ b/testcases/kernel/syscalls/setegid/setegid02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that setegid() fails with EPERM when the calling process is not * privileged and egid does not match the current real group ID, * current effective group ID, or current saved set-group-ID. diff --git a/testcases/kernel/syscalls/setfsgid/setfsgid01.c b/testcases/kernel/syscalls/setfsgid/setfsgid01.c index 5b594b2d..4724b5be 100755 --- a/testcases/kernel/syscalls/setfsgid/setfsgid01.c +++ b/testcases/kernel/syscalls/setfsgid/setfsgid01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that setfsgid() correctly updates the filesystem group ID * to the value given in fsgid argument. */ diff --git a/testcases/kernel/syscalls/setfsgid/setfsgid02.c b/testcases/kernel/syscalls/setfsgid/setfsgid02.c index 2a0b0c55..ae26a4ad 100755 --- a/testcases/kernel/syscalls/setfsgid/setfsgid02.c +++ b/testcases/kernel/syscalls/setfsgid/setfsgid02.c @@ -7,12 +7,10 @@ */ /*\ - * [Description] - * * Testcase for setfsgid() syscall to check that * * - privileged user can change a filesystem group ID different from saved - * value of previous setfsgid() call + * value of previous setfsgid() call * - unprivileged user cannot change it */ diff --git a/testcases/kernel/syscalls/setfsuid/setfsuid01.c b/testcases/kernel/syscalls/setfsuid/setfsuid01.c index 9805e38b..b23300f4 100755 --- a/testcases/kernel/syscalls/setfsuid/setfsuid01.c +++ b/testcases/kernel/syscalls/setfsuid/setfsuid01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that setfsuid() correctly updates the filesystem user ID * to the value given in fsuid argument. */ diff --git a/testcases/kernel/syscalls/setfsuid/setfsuid02.c b/testcases/kernel/syscalls/setfsuid/setfsuid02.c index b81f307f..6f46a982 100755 --- a/testcases/kernel/syscalls/setfsuid/setfsuid02.c +++ b/testcases/kernel/syscalls/setfsuid/setfsuid02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that setfsuid() syscall fails if an invalid fsuid is given. */ diff --git a/testcases/kernel/syscalls/setfsuid/setfsuid03.c b/testcases/kernel/syscalls/setfsuid/setfsuid03.c index ee06e937..b7f941bb 100755 --- a/testcases/kernel/syscalls/setfsuid/setfsuid03.c +++ b/testcases/kernel/syscalls/setfsuid/setfsuid03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that setfsuid() correctly updates the filesystem uid * when caller is a non-root user and provided fsuid matches * caller's real user ID. diff --git a/testcases/kernel/syscalls/setfsuid/setfsuid04.c b/testcases/kernel/syscalls/setfsuid/setfsuid04.c index 8585d620..e1525a73 100755 --- a/testcases/kernel/syscalls/setfsuid/setfsuid04.c +++ b/testcases/kernel/syscalls/setfsuid/setfsuid04.c @@ -62,7 +62,7 @@ int main(int ac, char **av) setup(); - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid < 0) tst_brkm(TBROK, cleanup, "Fork failed"); @@ -106,7 +106,7 @@ static void do_master_child(void) /* Test 2: Check a son process cannot open the file * with RDWR permissions. */ - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid < 0) { perror("Fork failed"); exit(TFAIL); diff --git a/testcases/kernel/syscalls/setgid/setgid01.c b/testcases/kernel/syscalls/setgid/setgid01.c index 91d61526..65c16228 100755 --- a/testcases/kernel/syscalls/setgid/setgid01.c +++ b/testcases/kernel/syscalls/setgid/setgid01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * @@ -6,8 +6,6 @@ * CO-PILOT : Dave Fenner */ /*\ - * [Description] - * * Calls setgid() with current gid and expects success. */ diff --git a/testcases/kernel/syscalls/setgid/setgid02.c b/testcases/kernel/syscalls/setgid/setgid02.c index ff6791a1..93684ef8 100755 --- a/testcases/kernel/syscalls/setgid/setgid02.c +++ b/testcases/kernel/syscalls/setgid/setgid02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test if setgid() system call sets errno to EPERM correctly. * * [Algorithm] diff --git a/testcases/kernel/syscalls/setgroups/.gitignore b/testcases/kernel/syscalls/setgroups/.gitignore index 9de92824..0649a342 100755 --- a/testcases/kernel/syscalls/setgroups/.gitignore +++ b/testcases/kernel/syscalls/setgroups/.gitignore @@ -4,3 +4,5 @@ /setgroups02_16 /setgroups03 /setgroups03_16 +/setgroups04 +/setgroups04_16 diff --git a/testcases/kernel/syscalls/setgroups/Makefile b/testcases/kernel/syscalls/setgroups/Makefile index b2bb1e00..41160978 100755 --- a/testcases/kernel/syscalls/setgroups/Makefile +++ b/testcases/kernel/syscalls/setgroups/Makefile @@ -3,6 +3,9 @@ top_srcdir ?= ../../../.. +# Remove after rewriting setgroups04.c to the new API. +USE_LEGACY_COMPAT_16_H := 1 + include $(top_srcdir)/include/mk/testcases.mk include $(abs_srcdir)/../utils/compat_16.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/setgroups/setgroups01.c b/testcases/kernel/syscalls/setgroups/setgroups01.c index 9a5b77e9..f8e7d301 100755 --- a/testcases/kernel/syscalls/setgroups/setgroups01.c +++ b/testcases/kernel/syscalls/setgroups/setgroups01.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Check the basic functionality of the setgroups() system call. */ diff --git a/testcases/kernel/syscalls/setgroups/setgroups02.c b/testcases/kernel/syscalls/setgroups/setgroups02.c index 2b7f95c8..e636442e 100755 --- a/testcases/kernel/syscalls/setgroups/setgroups02.c +++ b/testcases/kernel/syscalls/setgroups/setgroups02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that root process can setgroups() supplementary group ID and verify * that getgroups() returns the previously set ID. */ diff --git a/testcases/kernel/syscalls/setgroups/setgroups03.c b/testcases/kernel/syscalls/setgroups/setgroups03.c index fbf8de0b..8d17f5ba 100755 --- a/testcases/kernel/syscalls/setgroups/setgroups03.c +++ b/testcases/kernel/syscalls/setgroups/setgroups03.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Test for EINVAL, EPERM, EFAULT errors. * * - setgroups() fails with EINVAL if the size argument value is > NGROUPS. diff --git a/testcases/kernel/syscalls/setgroups/setgroups04.c b/testcases/kernel/syscalls/setgroups/setgroups04.c new file mode 100644 index 00000000..71e2ca10 --- /dev/null +++ b/testcases/kernel/syscalls/setgroups/setgroups04.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) Bull S.A. 2001 + * Copyright (c) International Business Machines Corp., 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Test Name: setgroups04 + * + * Test Description: + * Verify that, setgroups() fails with -1 and sets errno to EFAULT if the list has an invalid address. + * + * Expected Result: + * setgroups() should fail with return value -1 and set expected errno. + * + * Algorithm: + * Setup: + * Setup signal handling. + * Pause for SIGUSR1 if option specified. + * + * Test: + * Loop if the proper options are given. + * Execute system call + * Check return code, if system call failed (return=-1) + * if errno set == expected errno + * Issue sys call fails with expected return value and errno. + * Otherwise, + * Issue sys call fails with unexpected errno. + * Otherwise, + * Issue sys call returns unexpected value. + * + * Cleanup: + * Print errno log and/or timing stats if options given + * + * Usage: + * setgroups04 [-c n] [-e] [-i n] [-I x] [-P x] [-t] + * where, -c n : Run n copies concurrently. + * -f : Turn off functionality Testing. + * -i n : Execute test n times. + * -I x : Execute test for x seconds. + * -P x : Pause for x seconds between iterations. + * -t : Turn on syscall timing. + * + * HISTORY + * 05/2002 Ported by André Merlier + * + * RESTRICTIONS: + * none. + * + */ +#include +#include +#include +#include +#include +#include + +#include "test.h" + +/* + * Don't forget to remove USE_LEGACY_COMPAT_16_H from Makefile after + * rewriting this test to the new API. + */ +#include "compat_16.h" + +TCID_DEFINE(setgroups04); +int TST_TOTAL = 1; + +GID_T groups_list[NGROUPS]; + +void setup(); /* setup function for the test */ +void cleanup(); /* cleanup function for the test */ + +int main(int ac, char **av) +{ + int lc; + int gidsetsize; /* total no. of groups */ + char *test_desc; /* test specific error message */ + + tst_parse_opts(ac, av, NULL, NULL); + + /* Perform setup for test */ + setup(); + + for (lc = 0; TEST_LOOPING(lc); lc++) { + + tst_count = 0; + + gidsetsize = NGROUPS; + test_desc = "EFAULT"; + + /* + * Call setgroups() to test condition + * verify that it fails with -1 return value and + * sets appropriate errno. + */ + TEST(SETGROUPS(cleanup, gidsetsize, sbrk(0))); + + if (TEST_RETURN != -1) { + tst_resm(TFAIL, "setgroups() returned %ld, " + "expected -1, errno=%d", TEST_RETURN, + EFAULT); + } else { + + if (TEST_ERRNO == EFAULT) { + tst_resm(TPASS, + "setgroups() fails with expected " + "error EFAULT errno:%d", TEST_ERRNO); + } else { + tst_resm(TFAIL, "setgroups() fails, %s, " + "errno=%d, expected errno=%d", + test_desc, TEST_ERRNO, EFAULT); + } + } + + } + + cleanup(); + tst_exit(); + +} + +/* + * setup() + */ +void setup(void) +{ + tst_require_root(); + + tst_sig(NOFORK, DEF_HANDLER, cleanup); + + TEST_PAUSE; + +} + +/* + * cleanup() + */ +void cleanup(void) +{ + +} diff --git a/testcases/kernel/syscalls/setitimer/setitimer01.c b/testcases/kernel/syscalls/setitimer/setitimer01.c index d12abe90..22b89e47 100755 --- a/testcases/kernel/syscalls/setitimer/setitimer01.c +++ b/testcases/kernel/syscalls/setitimer/setitimer01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Spawn a child, verify that setitimer() syscall passes and it ends up * counting inside expected boundaries. Then verify from the parent that * the syscall sent the correct signal to the child. @@ -20,9 +18,10 @@ #include "tst_test.h" #include "lapi/syscalls.h" #include "tst_safe_clocks.h" +#include "tst_timer.h" static struct timeval tv; -static struct itimerval *value, *ovalue; +static struct __kernel_old_itimerval *value, *ovalue; static volatile unsigned long sigcnt; static long time_step; static long time_sec; @@ -38,11 +37,6 @@ static struct tcase { {ITIMER_PROF, "ITIMER_PROF", SIGPROF}, }; -static int sys_setitimer(int which, void *new_value, void *old_value) -{ - return tst_syscall(__NR_setitimer, which, new_value, old_value); -} - static void sig_routine(int signo LTP_ATTRIBUTE_UNUSED) { sigcnt++; diff --git a/testcases/kernel/syscalls/setitimer/setitimer02.c b/testcases/kernel/syscalls/setitimer/setitimer02.c index b012d71f..7c38ad9f 100755 --- a/testcases/kernel/syscalls/setitimer/setitimer02.c +++ b/testcases/kernel/syscalls/setitimer/setitimer02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check that setitimer() call fails: * * 1. EFAULT with invalid itimerval pointer @@ -19,13 +17,9 @@ #include #include "tst_test.h" #include "lapi/syscalls.h" +#include "tst_timer.h" -static struct itimerval *value, *ovalue; - -static int sys_setitimer(int which, void *new_value, void *old_value) -{ - return tst_syscall(__NR_setitimer, which, new_value, old_value); -} +static struct __kernel_old_itimerval *value, *ovalue; static void verify_setitimer(unsigned int i) { @@ -55,8 +49,8 @@ static struct tst_test test = { .test = verify_setitimer, .setup = setup, .bufs = (struct tst_buffers[]) { - {&value, .size = sizeof(struct itimerval)}, - {&ovalue, .size = sizeof(struct itimerval)}, + {&value, .size = sizeof(struct __kernel_old_itimerval)}, + {&ovalue, .size = sizeof(struct __kernel_old_itimerval)}, {} } }; diff --git a/testcases/kernel/syscalls/setpgid/setpgid01.c b/testcases/kernel/syscalls/setpgid/setpgid01.c index 89c9a377..59ec372a 100755 --- a/testcases/kernel/syscalls/setpgid/setpgid01.c +++ b/testcases/kernel/syscalls/setpgid/setpgid01.c @@ -1,150 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * 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: setpgid01.c,v 1.7 2009/11/02 13:57:18 subrata_modak Exp $ */ - -/* - * Description: - * Verify that: - * 1. Basic functionality test for setpgid(2). - * 2. Check functioning of setpgid(2) with pid = 0 and pgid = 0. + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -#include -#include -#include -#include -#include -#include "test.h" +/*\ + * Verify basic setpgid() functionality, re-setting group ID inside both parent + * and child. In the first case, we obtain getpgrp() and set it. In the second + * case, we use setpgid(0, 0). + */ -static void setup(void); -static void cleanup(void); - -char *TCID = "setpgid01"; - -static void setpgid_test1(void); -static void setpgid_test2(void); -static void (*testfunc[])(void) = { setpgid_test1, setpgid_test2}; -int TST_TOTAL = ARRAY_SIZE(testfunc); - -int main(int ac, char **av) -{ - int i, lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - for (i = 0; i < TST_TOTAL; i++) - (*testfunc[i])(); - } - - cleanup(); - tst_exit(); -} +#include "tst_test.h" static void setpgid_test1(void) { pid_t pgid, pid; - pgid = getpgrp(); - pid = getpid(); + pgid = TST_EXP_PID(getpgrp()); + pid = TST_EXP_PID(getpid()); - TEST(setpgid(pid, pgid)); - if (TEST_RETURN == -1 || getpgrp() != pgid) { - tst_resm(TFAIL | TTERRNO, "test setpgid(%d, %d) fail", - pid, pgid); - } else { - tst_resm(TPASS, "test setpgid(%d, %d) success", pid, pgid); - } -} - -static int wait4child(pid_t child) -{ - int status; - - if (waitpid(child, &status, 0) == -1) - tst_resm(TBROK|TERRNO, "waitpid"); - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else - return status; + TST_EXP_PASS(setpgid(pid, pgid)); + TST_EXP_EQ_LI(pgid, getpgrp()); } static void setpgid_test2(void) { - int ret; - pid_t pgid, pid; + pid_t pgid; - pid = FORK_OR_VFORK(); - if (pid == -1) - tst_brkm(TBROK | TERRNO, cleanup, "fork()"); - - if (pid != 0) { - ret = wait4child(pid); - } else { - pid = getpid(); - TEST(setpgid(0, 0)); - pgid = getpgrp(); - if (TEST_RETURN == -1) { - fprintf(stderr, "setpgid(0, 0) fails in " - "child process: %s\n", strerror(TEST_ERRNO)); - exit(1); - } else if (pgid != pid) { - fprintf(stderr, "setpgid(0, 0) fails to make PGID" - "equal to PID\n"); - exit(1); - } else { - exit(0); - } + if (!SAFE_FORK()) { + pgid = TST_EXP_PID(getpid()); + TST_EXP_PASS(setpgid(0, 0)); + TST_EXP_EQ_LI(pgid, getpgrp()); } - - if (ret == 0) - tst_resm(TPASS, "test setpgid(0, 0) success"); - else - tst_resm(TFAIL, "test setpgid(0, 0) fail"); } - -static void setup(void) +static void run(void) { - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; + setpgid_test1(); + setpgid_test2(); } -static void cleanup(void) -{ -} +static struct tst_test test = { + .test_all = run, + .forks_child = 1, +}; diff --git a/testcases/kernel/syscalls/setpgid/setpgid02.c b/testcases/kernel/syscalls/setpgid/setpgid02.c index b380d7df..978d255e 100755 --- a/testcases/kernel/syscalls/setpgid/setpgid02.c +++ b/testcases/kernel/syscalls/setpgid/setpgid02.c @@ -6,15 +6,13 @@ */ /*\ - * [Description] - * * Verify that setpgid(2) syscall fails with: * * - EINVAL when given pgid is less than 0. * - ESRCH when pid is not the calling process and not a child of - * the calling process. + * the calling process. * - EPERM when an attempt was made to move a process into a nonexisting - * process group. + * process group. */ #include diff --git a/testcases/kernel/syscalls/setpgid/setpgid03.c b/testcases/kernel/syscalls/setpgid/setpgid03.c index 9ce2603d..ed72fd34 100755 --- a/testcases/kernel/syscalls/setpgid/setpgid03.c +++ b/testcases/kernel/syscalls/setpgid/setpgid03.c @@ -7,17 +7,13 @@ */ /*\ - * [Description] - * * Tests setpgid(2) errors: * * - EPERM The process specified by pid must not be a session leader. - * * - EPERM The calling process, process specified by pid and the target - * process group must be in the same session. - * + * process group must be in the same session. * - EACCESS Proccess cannot change process group ID of a child after child - * has performed exec() + * has performed exec() */ #include diff --git a/testcases/kernel/syscalls/setpgrp/setpgrp01.c b/testcases/kernel/syscalls/setpgrp/setpgrp01.c index 3fdb8cb4..543106f1 100755 --- a/testcases/kernel/syscalls/setpgrp/setpgrp01.c +++ b/testcases/kernel/syscalls/setpgrp/setpgrp01.c @@ -92,7 +92,7 @@ void setup(void) * Make sure current process is NOT a session or pgrp leader */ if (getpgrp() == getpid()) { - if ((pid = FORK_OR_VFORK()) == -1) { + if ((pid = tst_fork()) == -1) { tst_brkm(TBROK, cleanup, "fork() in setup() failed - errno %d", errno); } diff --git a/testcases/kernel/syscalls/setpgrp/setpgrp02.c b/testcases/kernel/syscalls/setpgrp/setpgrp02.c index f1f6ce72..732364e6 100755 --- a/testcases/kernel/syscalls/setpgrp/setpgrp02.c +++ b/testcases/kernel/syscalls/setpgrp/setpgrp02.c @@ -1,130 +1,33 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * NAME - * setpgrp02.c - * - * DESCRIPTION - * Testcase to check the basic functionality of the setpgrp(2) syscall. - * - * ALGORITHM - * Check the values that setpgrp() and getpgrp() return. The setpgrp() - * returns 0 on success in Linux, but, in DYNIX/ptx this call returns - * the new pgid. - * - * USAGE: - * setpgrp02 [-c n] [-f] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * HISTORY - * 07/2001 Ported by Wayne Boyer - * - * RESTRICTIONS - * None + * Copyright (c) International Business Machines Corp., 2001 + * 07/2001 Ported by Wayne Boyer + * Copyright (c) Linux Test Project, 2001-2016 + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -#include -#include -#include "test.h" -char *TCID = "setpgrp02"; -int TST_TOTAL = 1; +/*\ + * Testcase to check the basic functionality of the setpgrp(2) syscall. + */ -void setup(void); -void cleanup(void); +#include +#include "tst_test.h" -int main(int ac, char **av) +static void verify_setpgrp(void) { - int lc; + if (!SAFE_FORK()) { + int oldpgrp = getpgrp(); - int pid, oldpgrp; - int e_code, status, retval = 0; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - /* reset tst_count in case we are looping */ - tst_count = 0; - - if ((pid = FORK_OR_VFORK()) == -1) { - tst_brkm(TBROK, cleanup, "fork() failed"); - } - - if (pid == 0) { /* child */ - oldpgrp = getpgrp(); - - TEST(setpgrp()); - - if (TEST_RETURN != 0) { - retval = 1; - tst_resm(TFAIL, "setpgrp() FAILED, errno:%d", - errno); - continue; - } - - if (getpgrp() == oldpgrp) { - retval = 1; - tst_resm(TFAIL, "setpgrp() FAILED to set " - "new group id"); - continue; - } else { - tst_resm(TPASS, "functionality is correct"); - } - exit(retval); - } else { /* parent */ - /* wait for the child to finish */ - wait(&status); - /* make sure the child returned a good exit status */ - e_code = status >> 8; - if ((e_code != 0) || (retval != 0)) { - tst_resm(TFAIL, "Failures reported above"); - } - cleanup(); - } + TST_EXP_PASS(setpgrp()); + if (getpgrp() == oldpgrp) + tst_res(TFAIL, "setpgrp() FAILED to set new group id"); + else + tst_res(TPASS, "functionality is correct"); } - tst_exit(); } -/* - * setup() - performs all ONE TIME setup for this test. - */ -void setup(void) -{ - - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; -} - -/* - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - */ -void cleanup(void) -{ - -} +static struct tst_test test = { + .test_all = verify_setpgrp, + .forks_child = 1 +}; diff --git a/testcases/kernel/syscalls/setregid/setregid01.c b/testcases/kernel/syscalls/setregid/setregid01.c index 741028a4..58f87495 100755 --- a/testcases/kernel/syscalls/setregid/setregid01.c +++ b/testcases/kernel/syscalls/setregid/setregid01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * @@ -16,8 +16,6 @@ */ /*\ - * [Description] - * * Verify the basic functionality of setregid(2) system call. */ diff --git a/testcases/kernel/syscalls/setregid/setregid02.c b/testcases/kernel/syscalls/setregid/setregid02.c index 0210f485..fed60a89 100755 --- a/testcases/kernel/syscalls/setregid/setregid02.c +++ b/testcases/kernel/syscalls/setregid/setregid02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 * diff --git a/testcases/kernel/syscalls/setregid/setregid03.c b/testcases/kernel/syscalls/setregid/setregid03.c index 467bc3bb..fca8ecfa 100755 --- a/testcases/kernel/syscalls/setregid/setregid03.c +++ b/testcases/kernel/syscalls/setregid/setregid03.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 * diff --git a/testcases/kernel/syscalls/setregid/setregid04.c b/testcases/kernel/syscalls/setregid/setregid04.c index dbeb98ae..19261dc1 100755 --- a/testcases/kernel/syscalls/setregid/setregid04.c +++ b/testcases/kernel/syscalls/setregid/setregid04.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 * diff --git a/testcases/kernel/syscalls/setresgid/setresgid01.c b/testcases/kernel/syscalls/setresgid/setresgid01.c index d66c4226..facb918c 100755 --- a/testcases/kernel/syscalls/setresgid/setresgid01.c +++ b/testcases/kernel/syscalls/setresgid/setresgid01.c @@ -1,216 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. 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. - * - * 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. - * + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -/********************************************************** - * - * TEST IDENTIFIER : setresgid01 - * - * EXECUTED BY : root / superuser - * - * TEST TITLE : Checking functionality of setresgid(2) - * - * TEST CASE TOTAL : 5 - * - * AUTHOR : Madhu T L - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * DESCRIPTION - * Verify that, - * 1. setresgid(2) is successful for setresgid(-1, -1, -1) - * 2. setresgid(2) is successful for setresgid(-1, -1, nobody) - * 3. setresgid(2) is successful for setresgid(-1, nobody, -1) - * 4. setresgid(2) is successful for setresgid(nobody, -1, -1) - * 5. setresgid(2) is successful for setresgid(root, root, root) - * - * Setup: - * Setup signal handling. - * Test caller is superuser - * Check existence of root and nobody user id's - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return value and functionality, if success, - * Issue PASS message - * Otherwise, - * Issue FAIL message - * - * Cleanup: - * Print errno log and/or timing stats if options given - * - * USAGE: - * setresgid01 [-c n] [-e] [-f] [-h] [-i n] [-I x] [-p] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -f : Turn off functional testing - * -h : Show help screen - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -p : Pause for SIGUSR1 before starting - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - ****************************************************************/ -#define _GNU_SOURCE 1 -#include +/*\ + * Verify that setresgid() syscall correctly sets real user ID, effective user + * ID and the saved set-user ID in the calling process. + */ + +#define _GNU_SOURCE + #include -#include -#include -#include "test.h" -#include "safe_macros.h" -#include "compat_16.h" +#include "tst_test.h" +#include "compat_tst_16.h" -#define EXP_RET_VAL 0 - -struct test_case_t { /* test case structure */ - uid_t *rgid; /* real GID */ - uid_t *egid; /* effective GID */ - uid_t *sgid; /* saved GID */ - struct passwd *exp_rgid; /* Expected real GID */ - struct passwd *exp_egid; /* Expected effective GID */ - struct passwd *exp_sgid; /* Expected saved GID */ - char *desc; /* Test description */ +struct tcase { + uid_t *rgid; + uid_t *egid; + uid_t *sgid; + uid_t *exp_rgid; + uid_t *exp_egid; + uid_t *exp_sgid; }; -TCID_DEFINE(setresgid01); -static int testno; -static struct passwd nobody, root; -static uid_t nobody_gid, root_gid, neg = -1; +static uid_t nobody_gid; +static uid_t root_gid; +static uid_t neg = -1; -static int test_functionality(uid_t, uid_t, uid_t); -static void setup(void); -static void cleanup(void); - -/* Don't change order of these test cases */ -static struct test_case_t tdat[] = { - {&neg, &neg, &neg, &root, &root, &root, - "setresgid(-1, -1, -1)"}, - {&neg, &neg, &nobody.pw_gid, &root, &root, &nobody, - "setresgid(-1, -1, nobody)"}, - {&neg, &nobody.pw_gid, &neg, &root, &nobody, &nobody, - "setresgid(-1, nobody, -1)"}, - {&nobody.pw_gid, &neg, &neg, &nobody, &nobody, &nobody, - "setresgid(nobody, -1, -1)"}, - {&root.pw_gid, &root.pw_gid, &root.pw_gid, &root, &root, &root, - "setresgid(root, root, root)"}, +static struct tcase tcases[] = { + { + &neg, &neg, &neg, + &root_gid, &root_gid, &root_gid, + }, + { + &neg, &neg, &nobody_gid, + &root_gid, &root_gid, &nobody_gid, + }, + { + &neg, &nobody_gid, &neg, + &root_gid, &nobody_gid, &nobody_gid, + }, + { + &nobody_gid, &neg, &neg, + &nobody_gid, &nobody_gid, &nobody_gid, + }, + { + &root_gid, &root_gid, &root_gid, + &root_gid, &root_gid, &root_gid, + }, }; -int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); - -int main(int argc, char **argv) +static void run(unsigned int n) { - int lc; + struct tcase *tc = &tcases[n]; - tst_parse_opts(argc, argv, NULL, NULL); + uid_t cur_rgid; + uid_t cur_egid; + uid_t cur_sgid; - setup(); + TST_EXP_PASS(SETRESGID(*tc->rgid, *tc->egid, *tc->sgid)); + if (!TST_PASS) + return; - for (lc = 0; TEST_LOOPING(lc); lc++) { - /* reset tst_count in case we are looping */ - tst_count = 0; + SAFE_GETRESGID(&cur_rgid, &cur_egid, &cur_sgid); - for (testno = 0; testno < TST_TOTAL; ++testno) { - - TEST(SETRESGID(cleanup, *tdat[testno].rgid, *tdat[testno].egid, - *tdat[testno].sgid)); - - if (TEST_RETURN == EXP_RET_VAL) { - if (!test_functionality - (tdat[testno].exp_rgid->pw_gid, - tdat[testno].exp_egid->pw_gid, - tdat[testno].exp_sgid->pw_gid)) { - - tst_resm(TPASS, "Test for %s " - "successful", - tdat[testno].desc); - } else { - tst_resm(TFAIL, "Functionality test " - "for %s failed", - tdat[testno].desc); - } - } else { - tst_resm(TFAIL, "Test for %s failed; returned" - " %ld (expected %d), errno %d (expected" - " 0)", tdat[testno].desc, - TEST_RETURN, EXP_RET_VAL, TEST_ERRNO); - } - } - } - cleanup(); - - tst_exit(); + TST_EXP_EQ_LI(*tc->exp_rgid, cur_rgid); + TST_EXP_EQ_LI(*tc->exp_egid, cur_egid); + TST_EXP_EQ_LI(*tc->exp_sgid, cur_sgid); } -static int test_functionality(uid_t exp_rgid, uid_t exp_egid, uid_t exp_sgid) +static void setup(void) { - uid_t cur_rgid, cur_egid, cur_sgid; + struct passwd *pwd_buf; - /* Get current real, effective and saved group id's */ - SAFE_GETRESGID(cleanup, &cur_rgid, &cur_egid, &cur_sgid); + pwd_buf = SAFE_GETPWNAM("root"); + GID16_CHECK(pwd_buf->pw_gid, "setresgid"); + root_gid = pwd_buf->pw_gid; - if ((cur_rgid == exp_rgid) && (cur_egid == exp_egid) - && (cur_sgid == exp_sgid)) { - return 0; - } - return 1; + pwd_buf = SAFE_GETPWNAM("nobody"); + GID16_CHECK(pwd_buf->pw_gid, "setresgid"); + nobody_gid = pwd_buf->pw_gid; } -/* - * setup() - * performs all ONE TIME setup for this test - */ -void setup(void) -{ - struct passwd *passwd_p; - - tst_require_root(); - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - if ((passwd_p = getpwnam("root")) == NULL) { - tst_brkm(TBROK, NULL, "getpwnam() failed for root"); - - } - root = *passwd_p; - GID16_CHECK((root_gid = root.pw_gid), "setresgid", cleanup) - - if ((passwd_p = getpwnam("nobody")) == NULL) { - tst_brkm(TBROK, NULL, "nobody user id doesn't exist"); - - } - nobody = *passwd_p; - GID16_CHECK((nobody_gid = nobody.pw_gid), "setresgid", cleanup) - - /* Pause if that option was specified - * TEST_PAUSE contains the code to fork the test with the -c option. - */ - TEST_PAUSE; -} - -/* - * cleanup() - * performs all ONE TIME cleanup for this test at - * completion or premature exit - */ -void cleanup(void) -{ - -} +static struct tst_test test = { + .test = run, + .setup = setup, + .tcnt = ARRAY_SIZE(tcases), + .needs_root = 1, +}; diff --git a/testcases/kernel/syscalls/setresgid/setresgid02.c b/testcases/kernel/syscalls/setresgid/setresgid02.c index 2893aa54..cca4c46b 100755 --- a/testcases/kernel/syscalls/setresgid/setresgid02.c +++ b/testcases/kernel/syscalls/setresgid/setresgid02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that setresgid() will successfully set the expected GID when called * by root with the following combinations of arguments: * diff --git a/testcases/kernel/syscalls/setresgid/setresgid03.c b/testcases/kernel/syscalls/setresgid/setresgid03.c index 4f6bc6aa..8d827d17 100755 --- a/testcases/kernel/syscalls/setresgid/setresgid03.c +++ b/testcases/kernel/syscalls/setresgid/setresgid03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that setresgid() fails with EPERM if unprivileged user tries to set * process group ID which requires higher permissions. */ diff --git a/testcases/kernel/syscalls/setresgid/setresgid04.c b/testcases/kernel/syscalls/setresgid/setresgid04.c index 4d8e9685..9ba013eb 100755 --- a/testcases/kernel/syscalls/setresgid/setresgid04.c +++ b/testcases/kernel/syscalls/setresgid/setresgid04.c @@ -1,101 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Fujitsu Ltd. - * Author: Zeng Linggang - * - * 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. + * Copyright (c) Zeng Linggang + * Copyright (C) 2024 SUSE LLC Andrea Cervesato */ -/* - * Test Description: - * Verify that, - * File system GID is always set to the same value as the (possibly new) - * effective GID. + +/*\ + * Verify that setresgid() syscall always sets the file system GID to the same + * value as the new effective GID. */ #define _GNU_SOURCE -#include -#include #include -#include -#include "test.h" -#include "safe_macros.h" -#include "compat_16.h" +#include "tst_test.h" +#include "compat_tst_16.h" -TCID_DEFINE(setresgid04); -int TST_TOTAL = 1; static struct passwd *ltpuser; -static void setup(void); -static void setresgid_verify(void); -static void cleanup(void); -int main(int argc, char **argv) +static void run(void) { - int i, lc; + struct stat buf; - tst_parse_opts(argc, argv, NULL, NULL); + TST_EXP_PASS(SETRESGID(-1, ltpuser->pw_gid, -1)); - setup(); + SAFE_TOUCH("test_file", 0644, NULL); + SAFE_STAT("test_file", &buf); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - for (i = 0; i < TST_TOTAL; i++) - setresgid_verify(); - } - - cleanup(); - tst_exit(); + TST_EXP_EQ_LI(ltpuser->pw_gid, buf.st_gid); } static void setup(void) { - tst_require_root(); + ltpuser = SAFE_GETPWNAM("nobody"); - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - ltpuser = SAFE_GETPWNAM(cleanup, "nobody"); - - GID16_CHECK(ltpuser->pw_gid, "setresgid", cleanup) + GID16_CHECK(ltpuser->pw_gid, "setresgid"); } -static void setresgid_verify(void) -{ - struct stat buf; - - TEST(SETRESGID(cleanup, -1, ltpuser->pw_gid, -1)); - - if (TEST_RETURN != 0) { - tst_resm(TFAIL | TTERRNO, "setresgid failed unexpectedly"); - return; - } - - SAFE_TOUCH(cleanup, "test_file", 0644, NULL); - - SAFE_STAT(cleanup, "test_file", &buf); - - if (ltpuser->pw_gid == buf.st_gid) { - tst_resm(TPASS, "setresgid succeeded as expected"); - } else { - tst_resm(TFAIL, - "setresgid failed unexpectedly; egid(%d) - st_gid(%d)", - ltpuser->pw_gid, buf.st_gid); - } -} - -static void cleanup(void) -{ - tst_rmdir(); -} +static struct tst_test test = { + .test_all = run, + .setup = setup, + .needs_root = 1, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/setresuid/setresuid01.c b/testcases/kernel/syscalls/setresuid/setresuid01.c index 196e0376..b3cebcbd 100755 --- a/testcases/kernel/syscalls/setresuid/setresuid01.c +++ b/testcases/kernel/syscalls/setresuid/setresuid01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test setresuid() when executed by root. */ diff --git a/testcases/kernel/syscalls/setresuid/setresuid02.c b/testcases/kernel/syscalls/setresuid/setresuid02.c index 0931d2a2..6bd7a22d 100755 --- a/testcases/kernel/syscalls/setresuid/setresuid02.c +++ b/testcases/kernel/syscalls/setresuid/setresuid02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test that a non-root user can change the real, effective and saved uid * values through the setresuid system call. */ diff --git a/testcases/kernel/syscalls/setresuid/setresuid03.c b/testcases/kernel/syscalls/setresuid/setresuid03.c index f1518c75..f2ff0b7a 100755 --- a/testcases/kernel/syscalls/setresuid/setresuid03.c +++ b/testcases/kernel/syscalls/setresuid/setresuid03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test that the setresuid system call sets the proper errno values when * a non-root user attempts to change the real, effective or saved uid * to a value other than one of the current uid, the current effective uid diff --git a/testcases/kernel/syscalls/setresuid/setresuid04.c b/testcases/kernel/syscalls/setresuid/setresuid04.c index 57b290f3..cf10bad4 100755 --- a/testcases/kernel/syscalls/setresuid/setresuid04.c +++ b/testcases/kernel/syscalls/setresuid/setresuid04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that setresuid() behaves correctly with file permissions. * The test creates a file as ROOT with permissions 0644, does a setresuid * to change euid to a non-root user and tries to open the file with RDWR diff --git a/testcases/kernel/syscalls/setresuid/setresuid05.c b/testcases/kernel/syscalls/setresuid/setresuid05.c index efdcbd18..4193afdc 100755 --- a/testcases/kernel/syscalls/setresuid/setresuid05.c +++ b/testcases/kernel/syscalls/setresuid/setresuid05.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Fujitsu Ltd. * Author: Zeng Linggang @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that after updating euid with setresuid(), any file creation * also gets the new euid as its owner user ID. */ diff --git a/testcases/kernel/syscalls/setreuid/setreuid01.c b/testcases/kernel/syscalls/setreuid/setreuid01.c index 7c2b6d58..9442e7c5 100755 --- a/testcases/kernel/syscalls/setreuid/setreuid01.c +++ b/testcases/kernel/syscalls/setreuid/setreuid01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * Author: William Roske @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify the basic functionality of setreuid(2) system call when executed * as non-root user. */ diff --git a/testcases/kernel/syscalls/setreuid/setreuid02.c b/testcases/kernel/syscalls/setreuid/setreuid02.c index 2fa4cbc8..8f110ef7 100755 --- a/testcases/kernel/syscalls/setreuid/setreuid02.c +++ b/testcases/kernel/syscalls/setreuid/setreuid02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test setreuid() when executed by root. */ diff --git a/testcases/kernel/syscalls/setreuid/setreuid03.c b/testcases/kernel/syscalls/setreuid/setreuid03.c index e83a3586..f21a141d 100755 --- a/testcases/kernel/syscalls/setreuid/setreuid03.c +++ b/testcases/kernel/syscalls/setreuid/setreuid03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test setreuid() when executed by an unpriviledged user. */ diff --git a/testcases/kernel/syscalls/setreuid/setreuid04.c b/testcases/kernel/syscalls/setreuid/setreuid04.c index 06ffa32b..7407fa01 100755 --- a/testcases/kernel/syscalls/setreuid/setreuid04.c +++ b/testcases/kernel/syscalls/setreuid/setreuid04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that root user can change the real and effective uid to an * unprivileged user. */ diff --git a/testcases/kernel/syscalls/setreuid/setreuid05.c b/testcases/kernel/syscalls/setreuid/setreuid05.c index 4c1c94ee..9ab1f21d 100755 --- a/testcases/kernel/syscalls/setreuid/setreuid05.c +++ b/testcases/kernel/syscalls/setreuid/setreuid05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test the setreuid() feature, verifying the role of the saved-set-uid * and setreuid's effect on it. */ diff --git a/testcases/kernel/syscalls/setreuid/setreuid06.c b/testcases/kernel/syscalls/setreuid/setreuid06.c index 0a094fdc..55663570 100755 --- a/testcases/kernel/syscalls/setreuid/setreuid06.c +++ b/testcases/kernel/syscalls/setreuid/setreuid06.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that setreuid(2) syscall fails with EPERM errno when the calling * process is not privileged and a change other than * (i) swapping the effective user ID with the real user ID, or diff --git a/testcases/kernel/syscalls/setreuid/setreuid07.c b/testcases/kernel/syscalls/setreuid/setreuid07.c index 5faf3cb7..1bb2d842 100755 --- a/testcases/kernel/syscalls/setreuid/setreuid07.c +++ b/testcases/kernel/syscalls/setreuid/setreuid07.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Check if setreuid behaves correctly with file permissions. * The test creates a file as ROOT with permissions 0644, does a setreuid * and then tries to open the file with RDWR permissions. @@ -60,5 +58,6 @@ static struct tst_test test = { .setup = setup, .test_all = run, .needs_root = 1, - .forks_child = 1 + .forks_child = 1, + .needs_tmpdir = 1, }; diff --git a/testcases/kernel/syscalls/setrlimit/setrlimit01.c b/testcases/kernel/syscalls/setrlimit/setrlimit01.c index 7b133ef9..d0015353 100755 --- a/testcases/kernel/syscalls/setrlimit/setrlimit01.c +++ b/testcases/kernel/syscalls/setrlimit/setrlimit01.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "test.h" @@ -134,7 +135,7 @@ static void test2(void) * output will be saved to the logfile (instead of stdout) * when the testcase (parent) is run from the driver. */ - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid == -1) tst_brkm(TBROK, cleanup, "fork() failed"); @@ -218,7 +219,7 @@ static void test3(void) } for (i = 0; i < 20; i++) { - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid == -1) { if (errno != EAGAIN) { tst_resm(TWARN, "Expected EAGAIN got %d", @@ -251,13 +252,12 @@ static void test4(void) return; } - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid == -1) tst_brkm(TBROK, cleanup, "fork() failed"); if (pid == 0) { /* child */ - char *testbuf = NULL; - strcpy(testbuf, "abcd"); + raise(SIGSEGV); exit(0); } wait(&status); diff --git a/testcases/kernel/syscalls/setrlimit/setrlimit06.c b/testcases/kernel/syscalls/setrlimit/setrlimit06.c index 9ff515d8..e8e37818 100755 --- a/testcases/kernel/syscalls/setrlimit/setrlimit06.c +++ b/testcases/kernel/syscalls/setrlimit/setrlimit06.c @@ -1,22 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. + * Copyright (c) Linux Test Project, 2019-2025 * Author: Xiao Yang */ -/* - * Description: +/*\ * Set CPU time limit for a process and check its behavior - * after reaching CPU time limit. - * 1) Process got SIGXCPU after reaching soft limit of CPU time. - * 2) Process got SIGKILL after reaching hard limit of CPU time. + * after reaching CPU time limit * - * Note: - * This is also a regression test for the following kernel bug: - * 'c3bca5d450b62 ("posix-cpu-timers: Ensure set_process_cpu_timer is always evaluated")' + * - Process got SIGXCPU after reaching soft limit of CPU time + * - Process got SIGKILL after reaching hard limit of CPU time + * + * Test is also a regression test for kernel bug: + * c3bca5d450b62 ("posix-cpu-timers: Ensure set_process_cpu_timer is always evaluated") */ -#define _GNU_SOURCE +#define _LARGEFILE64_SOURCE #include #include #include @@ -27,6 +27,12 @@ #include #include "tst_test.h" +#include "lapi/resource.h" + +#define TEST_VARIANTS 2 + +static struct rlimit *rlim; +static struct rlimit64 *rlim_64; static int *end; @@ -37,6 +43,11 @@ static void sighandler(int sig) static void setup(void) { + rlim->rlim_cur = 1; + rlim->rlim_max = 2; + rlim_64->rlim_cur = 1; + rlim_64->rlim_max = 2; + SAFE_SIGNAL(SIGXCPU, sighandler); end = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE, @@ -58,12 +69,14 @@ static void verify_setrlimit(void) pid = SAFE_FORK(); if (!pid) { - struct rlimit rlim = { - .rlim_cur = 1, - .rlim_max = 2, - }; - - TEST(setrlimit(RLIMIT_CPU, &rlim)); + switch (tst_variant) { + case 0: + TEST(setrlimit(RLIMIT_CPU, rlim)); + break; + case 1: + TEST(setrlimit_u64(RLIMIT_CPU, rlim_64)); + break; + } if (TST_RET == -1) { tst_res(TFAIL | TTERRNO, "setrlimit(RLIMIT_CPU) failed"); @@ -72,7 +85,8 @@ static void verify_setrlimit(void) alarm(20); - while (1); + while (1) + ; } SAFE_WAITPID(pid, &status, 0); @@ -112,6 +126,12 @@ static void verify_setrlimit(void) static struct tst_test test = { .test_all = verify_setrlimit, .setup = setup, + .test_variants = TEST_VARIANTS, + .bufs = (struct tst_buffers []) { + {&rlim, .size = sizeof(*rlim)}, + {&rlim_64, .size = sizeof(*rlim_64)}, + {} + }, .cleanup = cleanup, .forks_child = 1, .tags = (const struct tst_tag[]) { diff --git a/testcases/kernel/syscalls/setsid/setsid01.c b/testcases/kernel/syscalls/setsid/setsid01.c index 08a7e259..197e865c 100755 --- a/testcases/kernel/syscalls/setsid/setsid01.c +++ b/testcases/kernel/syscalls/setsid/setsid01.c @@ -49,10 +49,6 @@ char *TCID = "setsid01"; int TST_TOTAL = 1; -#ifdef UCLINUX -static char *argv0; -#endif - void do_child_1(void); void do_child_2(void); void setup(void); @@ -68,12 +64,6 @@ int main(int ac, char **av) int lc; tst_parse_opts(ac, av, NULL, NULL); -#ifdef UCLINUX - argv0 = av[0]; - - maybe_run_child(&do_child_1, "n", 1); - maybe_run_child(&do_child_2, "n", 2); -#endif /* * perform global setup for the test @@ -90,22 +80,15 @@ int main(int ac, char **av) * and then it attached itself to another process * group and tries to setsid */ - pid = FORK_OR_VFORK(); + pid = tst_fork(); if (pid == 0) { - if ((pid = FORK_OR_VFORK()) == -1) { + if ((pid = tst_fork()) == -1) { tst_resm(TFAIL, "Fork failed"); } if (pid == 0) { -#ifdef UCLINUX - if (self_exec(argv0, "n", 1) < 0) { - tst_resm(TFAIL, "self_exec failed"); - - } -#else do_child_1(); -#endif } else { if (setpgid(0, 0) < 0) { tst_resm(TFAIL, @@ -161,17 +144,11 @@ void do_child_1(void) exno = 1; } - if ((pid = FORK_OR_VFORK()) == -1) { + if ((pid = tst_fork()) == -1) { tst_brkm(TFAIL, NULL, "Fork failed"); } if (pid == 0) { -#ifdef UCLINUX - if (self_exec(argv0, "n", 2) < 0) { - tst_brkm(TFAIL, NULL, "self_exec failed"); - } -#else do_child_2(); -#endif } else { retval = setpgid(0, getppid()); if (retval < 0) { diff --git a/testcases/kernel/syscalls/setsockopt/.gitignore b/testcases/kernel/syscalls/setsockopt/.gitignore index fd3235bb..5c05290a 100755 --- a/testcases/kernel/syscalls/setsockopt/.gitignore +++ b/testcases/kernel/syscalls/setsockopt/.gitignore @@ -7,3 +7,4 @@ /setsockopt07 /setsockopt08 /setsockopt09 +/setsockopt10 diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt01.c b/testcases/kernel/syscalls/setsockopt/setsockopt01.c index 4c48a0d9..1ded0cda 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt01.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt01.c @@ -1,13 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2001 - * 07/2001 John George - * Copyright (c) 2020 Martin Doucha + * Copyright (c) International Business Machines Corp., 2001 + * Author: John George + * Copyright (c) 2020 Martin Doucha */ -/* - * Test Description: - * Verify that setsockopt() returns the proper errno for various failure cases +/*\ + * Verify that setsockopt() fails and set errno: + * + * - EBADF on invalid file descriptor + * - ENOTSOCK on non-socket file descriptor + * - EFAULT on invalid option buffer + * - EINVAL on invalid optlen + * - ENOPROTOOPT on invalid level + * - ENOPROTOOPT on invalid option name (UDP) + * - ENOPROTOOPT on invalid option name (IP) + * - ENOPROTOOPT on invalid option name (TCP) */ #include diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt02.c b/testcases/kernel/syscalls/setsockopt/setsockopt02.c index 3349c997..15d86ae1 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt02.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt02.c @@ -2,7 +2,9 @@ /* * Copyright (c) 2017 Richard Palethorpe */ -/* Test for CVE-2017-7308 on a raw socket's ring buffer + +/*\ + * Test for CVE-2017-7308 on a raw socket's ring buffer * * Try to set tpacket_req3.tp_sizeof_priv to a value with the high bit set. So * that tp_block_size < tp_sizeof_priv. If the vulnerability is present then diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt03.c b/testcases/kernel/syscalls/setsockopt/setsockopt03.c index 210b7542..90d54661 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt03.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt03.c @@ -3,7 +3,8 @@ * Copyright (c) 2017 Richard Palethorpe * Based on repro-compatReleaseEntry.c by NCC group */ -/* + +/*\ * Test for CVE-2016-4997 * * For a full explanation of how the vulnerability works see: @@ -11,7 +12,7 @@ * * The original vulnerability was present in the 32-bit compatibility system * call, so the test should be compiled with -m32 and run on a 64-bit kernel. - * For simplicities sake the test requests root privliges instead of creating + * For simplicities sake the test requests root privileges instead of creating * a user namespace. */ @@ -45,9 +46,8 @@ struct payload { static void setup(void) { - if (tst_kernel_bits() == 32 || sizeof(long) > 4) - tst_res(TCONF, - "The vulnerability was only present in 32-bit compat mode"); + if (!tst_is_compat_mode()) + tst_res(TINFO, "The vulnerability was only present in 32-bit compat mode"); } static void run(void) diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt04.c b/testcases/kernel/syscalls/setsockopt/setsockopt04.c index 50d82346..7916314c 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt04.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt04.c @@ -3,7 +3,8 @@ * Copyright (c) 2019 SUSE LLC * Author: Christian Amann */ -/* Test for CVE-2016-9793 +/*\ + * CVE-2016-9793 * * With kernels between version 3.11 and 4.8 missing commit b98b0bc8 it * is possible to pass a very high unsigned integer as send buffer size diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt05.c b/testcases/kernel/syscalls/setsockopt/setsockopt05.c index 3263da98..35a3d1bd 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt05.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt05.c @@ -3,18 +3,14 @@ * Copyright (c) 2019 SUSE LLC */ -/* +/*\ * CVE-2017-1000112 * * Check that UDP fragmentation offload doesn't cause memory corruption * if the userspace process turns off UFO in between two send() calls. - * Kernel crash fixed in: * - * commit 85f1bd9a7b5a79d5baa8bf44af19658f7bf77bfa - * Author: Willem de Bruijn - * Date: Thu Aug 10 12:29:19 2017 -0400 - * - * udp: consistently apply ufo or fragmentation + * Kernel crash fixed in 4.13 + * 85f1bd9a7b5a ("udp: consistently apply ufo or fragmentation") */ #include diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt06.c b/testcases/kernel/syscalls/setsockopt/setsockopt06.c index 00dc69bf..2160b29e 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt06.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt06.c @@ -3,17 +3,14 @@ * Copyright (c) 2020 SUSE LLC */ -/* +/*\ * CVE-2016-8655 * * Check for race condition between packet_set_ring() and tp_version. On some - * kernels, this may lead to use-after-free. Kernel crash fixed in: + * kernels, this may lead to use-after-free. * - * commit 84ac7260236a49c79eede91617700174c2c19b0c - * Author: Philip Pettersson - * Date: Wed Nov 30 14:55:36 2016 -0800 - * - * packet: fix race condition in packet_set_ring + * Kernel crash fixed in 4.9 + * 84ac7260236a ("packet: fix race condition in packet_set_ring") */ #include @@ -111,7 +108,7 @@ static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 270, + .min_runtime = 270, .taint_check = TST_TAINT_W | TST_TAINT_D, .needs_kconfigs = (const char *[]) { "CONFIG_USER_NS=y", diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt07.c b/testcases/kernel/syscalls/setsockopt/setsockopt07.c index f6f94ad9..6eeec8a6 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt07.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt07.c @@ -3,20 +3,16 @@ * Copyright (c) 2020 SUSE LLC */ -/* +/*\ * CVE-2017-1000111 * * Check for race condition between packet_set_ring() and tp_reserve. * The race allows you to set tp_reserve bigger than ring buffer size. * While this will cause truncation of all incoming packets to 0 bytes, * sanity checks in tpacket_rcv() prevent any exploitable buffer overflows. - * Race fixed in: * - * commit c27927e372f0785f3303e8fad94b85945e2c97b7 (HEAD) - * Author: Willem de Bruijn - * Date: Thu Aug 10 12:41:58 2017 -0400 - * - * packet: fix tp_reserve race in packet_set_ring + * Race fixed in v4.13 + * c27927e372f0 ("packet: fix tp_reserve race in packet_set_ring") */ #include @@ -125,7 +121,7 @@ static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, - .max_runtime = 150, + .min_runtime = 150, .needs_kconfigs = (const char *[]) { "CONFIG_USER_NS=y", "CONFIG_NET_NS=y", diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt08.c b/testcases/kernel/syscalls/setsockopt/setsockopt08.c index 7f8243de..76ec1054 100755 --- a/testcases/kernel/syscalls/setsockopt/setsockopt08.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt08.c @@ -3,9 +3,8 @@ * Copyright (c) 2021 SUSE LLC * Based on reproducer by Nicolai Stange based on PoC Andy Nguyen */ + /*\ - * [Description] - * * This will reproduce the bug on x86_64 in 32bit compatibility * mode. It is most reliable with KASAN enabled. Otherwise it relies * on the out-of-bounds write corrupting something which leads to a @@ -38,20 +37,20 @@ * * * the mandatory struct compat_ipt_replace header, * * a single entry consisting of - * ** the mandatory compat_ipt_entry header - * ** a single 'state' match entry of appropriate size for - * controlling the out-of-bounds write when converting - * the target entry following next, - * ** a single 'REJECT' target entry. + * * the mandatory compat_ipt_entry header + * * a single 'state' match entry of appropriate size for + * controlling the out-of-bounds write when converting + * the target entry following next, + * * a single 'REJECT' target entry. * * The kernel will transform this into a buffer containing (in * this order) * * * a xt_table_info * * a single entry consisting of - * ** its ipt_entry header - * ** a single 'state' match entry - * ** followed by a single 'REJECT' target entry. + * * its ipt_entry header + * * a single 'state' match entry + * * followed by a single 'REJECT' target entry. * * The expected sizes for the 'state' match entries as well as the * 'REJECT' target are the size of the base header struct (32 bytes) @@ -70,7 +69,7 @@ * That is, the padding gets inserted unconditionally during the transformation, * independent of the actual values of ->u.user.match_size or * ->u.user.target_size and the result ends up getting layed out with proper - * alignment only if said values match the expectations. + * alignment only if said values match the expectations. * * That's not a problem in itself, but this unconditional insertion of padding * must be taken into account in the match_size calculation below. @@ -95,10 +94,8 @@ static void *buffer; void setup(void) { - if (tst_kernel_bits() == 32 || sizeof(long) > 4) { - tst_res(TINFO, - "The vulnerability was only present in 32-bit compat mode"); - } + if (!tst_is_compat_mode()) + tst_res(TINFO, "The vulnerability was only present in 32-bit compat mode"); tst_setup_netns(); } diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt09.c b/testcases/kernel/syscalls/setsockopt/setsockopt09.c index 9ed80e46..a4b6b931 100644 --- a/testcases/kernel/syscalls/setsockopt/setsockopt09.c +++ b/testcases/kernel/syscalls/setsockopt/setsockopt09.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Check for possible double free of rx_owner_map after switching packet * interface versions aka CVE-2021-22600. * diff --git a/testcases/kernel/syscalls/setsockopt/setsockopt10.c b/testcases/kernel/syscalls/setsockopt/setsockopt10.c new file mode 100644 index 00000000..f955f4e8 --- /dev/null +++ b/testcases/kernel/syscalls/setsockopt/setsockopt10.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 SUSE LLC Richard Palethorpe + */ +/*\ + * Reproducer for CVE-2023-0461 which is an exploitable use-after-free + * in a TLS socket. In fact it is exploitable in any User Level + * Protocol (ULP) which does not clone its context when accepting a + * connection. + * + * Because it does not clone the context, the child socket which is + * created on accept has a pointer to the listening socket's + * context. When the child is closed the parent's context is freed + * while it still has a reference to it. + * + * TLS can only be added to a socket which is connected. Not listening + * or disconnected, and a connected socket can not be set to + * listening. So we have to connect the socket, add TLS, then + * disconnect, then set it to listening. + * + * To my knowledge, setting a socket from open to disconnected + * requires a trick; we have to "connect" to an unspecified + * address. This could explain why the bug was not found earlier. + * + * The accepted fix was to disallow listening on sockets with a ULP + * set which does not have a clone function. + * + * The test uses two processes, first the child acts as a server so + * that the parent can create the TLS socket. Then the child connects + * to the parent's TLS socket. + * + * When we try to listen on the parent, the current kernel should + * return EINVAL. However if clone is implemented then this could + * become a valid operation. It is also quite easy to crash the kernel + * if we set some TLS options before doing a double free. + * + * commit 2c02d41d71f90a5168391b6a5f2954112ba2307c + * Author: Paolo Abeni + * Date: Tue Jan 3 12:19:17 2023 +0100 + * + * net/ulp: prevent ULP without clone op from entering the LISTEN status + */ + +#include "sched.h" +#include "tst_test.h" + +#ifdef HAVE_LINUX_TLS_H + +#include +#include + +#include "lapi/sched.h" +#include "lapi/socket.h" +#include "lapi/tcp.h" +#include "tst_checkpoint.h" +#include "tst_net.h" +#include "tst_safe_net.h" +#include "tst_taint.h" + +static struct tls12_crypto_info_aes_gcm_128 opts = { + .info = { + .version = TLS_1_2_VERSION, + .cipher_type = TLS_CIPHER_AES_GCM_128, + }, + .iv = { 'i', 'v' }, + .key = { 'k', 'e', 'y' }, + .salt = { 's', 'a', 'l', 't' }, + .rec_seq = { 'r', 'e', 'c', 's' }, +}; + +static struct sockaddr_in tcp0_addr, tcp1_addr; +static const struct sockaddr unspec_addr = { + .sa_family = AF_UNSPEC +}; + +static int tcp0_sk, tcp1_sk, tcp2_sk, tcp3_sk; + +static void setup(void) +{ + tst_init_sockaddr_inet(&tcp0_addr, "127.0.0.1", 0x7c90); + tst_init_sockaddr_inet(&tcp1_addr, "127.0.0.1", 0x7c91); +} + +static void cleanup(void) +{ + if (tcp0_sk > 0) + SAFE_CLOSE(tcp0_sk); + if (tcp1_sk > 0) + SAFE_CLOSE(tcp1_sk); + if (tcp2_sk > 0) + SAFE_CLOSE(tcp2_sk); + if (tcp3_sk > 0) + SAFE_CLOSE(tcp3_sk); +} + +static void child(void) +{ + tst_res(TINFO, "child: Listen for tcp1 connection"); + tcp0_sk = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); + SAFE_BIND(tcp0_sk, (struct sockaddr *)&tcp0_addr, sizeof(tcp0_addr)); + SAFE_LISTEN(tcp0_sk, 1); + TST_CHECKPOINT_WAKE(0); + + tcp3_sk = SAFE_ACCEPT(tcp0_sk, NULL, 0); + TST_CHECKPOINT_WAIT(1); + SAFE_CLOSE(tcp3_sk); + SAFE_CLOSE(tcp0_sk); + + tcp3_sk = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); + TST_CHECKPOINT_WAIT(2); + + tst_res(TINFO, "child: connect for tcp2 connection"); + TEST(connect(tcp3_sk, (struct sockaddr *)&tcp1_addr, sizeof(tcp1_addr))); + + if (TST_RET == -1) { + tst_res(TINFO | TTERRNO, "child: could not connect to tcp1"); + return; + } + + TST_CHECKPOINT_WAIT(3); +} + +static void run(void) +{ + const pid_t child_pid = SAFE_FORK(); + + if (child_pid == 0) { + child(); + return; + } + + tcp1_sk = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0); + TST_CHECKPOINT_WAIT(0); + + tst_res(TINFO, "parent: Connect for tcp0 connection"); + SAFE_CONNECT(tcp1_sk, (struct sockaddr *)&tcp0_addr, sizeof(tcp0_addr)); + TEST(setsockopt(tcp1_sk, SOL_TCP, TCP_ULP, "tls", 3)); + + if (TST_RET == -1 && TST_ERR == ENOENT) + tst_brk(TCONF | TTERRNO, "parent: setsockopt failed: The TLS module is probably not loaded"); + else if (TST_RET == -1) + tst_brk(TBROK | TTERRNO, "parent: setsockopt failed"); + + SAFE_SETSOCKOPT(tcp1_sk, SOL_TLS, TLS_TX, &opts, sizeof(opts)); + TST_CHECKPOINT_WAKE(1); + + tst_res(TINFO, "parent: Disconnect by setting unspec address"); + TEST(connect(tcp1_sk, &unspec_addr, sizeof(unspec_addr))); + if (TST_RET == -1) { + if (TST_ERR == EOPNOTSUPP) + tst_res(TPASS | TTERRNO, "parent: tls disallows disconnect"); + else + tst_res(TFAIL | TTERRNO, "parent: unexpected errno from connect"); + TST_CHECKPOINT_WAKE(2); + tst_reap_children(); + return; + } + SAFE_BIND(tcp1_sk, (struct sockaddr *)&tcp1_addr, sizeof(tcp1_addr)); + + TEST(listen(tcp1_sk, 1)); + + if (TST_RET == -1) { + if (TST_ERR == EINVAL) + tst_res(TPASS | TTERRNO, "parent: Can't listen on disconnected TLS socket"); + else + tst_res(TCONF | TTERRNO, "parent: Can't listen on disconnected TLS socket, but the errno is not EINVAL as expected"); + + TST_CHECKPOINT_WAKE(2); + tst_reap_children(); + return; + } + + tst_res(TINFO, "parent: Can listen on disconnected TLS socket"); + TST_CHECKPOINT_WAKE(2); + + tcp2_sk = SAFE_ACCEPT(tcp1_sk, NULL, 0); + SAFE_CLOSE(tcp2_sk); + + tst_res(TINFO, "parent: Attempting double free, because we set cipher options this should result in an crash"); + tst_flush(); + SAFE_CLOSE(tcp1_sk); + + TST_CHECKPOINT_WAKE(3); + tst_reap_children(); + sched_yield(); + + tst_res(TCONF, "parent: We're still here, maybe the kernel can clone the TLS-ULP context now?"); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .forks_child = 1, + .needs_checkpoints = 1, + .taint_check = TST_TAINT_W | TST_TAINT_D, + .needs_kconfigs = (const char *[]) { + "CONFIG_TLS", + NULL + }, + .tags = (const struct tst_tag[]) { + {"linux-git", "2c02d41d71f90"}, + {"CVE", "2023-0461"}, + {} + } +}; + +#else + +TST_TEST_TCONF("linux/tls.h missing, we assume your system is too old"); + +#endif diff --git a/testcases/kernel/syscalls/settimeofday/settimeofday01.c b/testcases/kernel/syscalls/settimeofday/settimeofday01.c index b7f84b00..e403fe40 100755 --- a/testcases/kernel/syscalls/settimeofday/settimeofday01.c +++ b/testcases/kernel/syscalls/settimeofday/settimeofday01.c @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * Ported to LTP 07/2001 John George - * Testcase to check the basic functionality of settimeofday(). + * Copyright (c) Linux Test Project, 2001-2024 + */ + +/*\ + * Check the basic functionality of settimeofday(). */ #include diff --git a/testcases/kernel/syscalls/settimeofday/settimeofday02.c b/testcases/kernel/syscalls/settimeofday/settimeofday02.c index 0fa8a147..4be44df7 100755 --- a/testcases/kernel/syscalls/settimeofday/settimeofday02.c +++ b/testcases/kernel/syscalls/settimeofday/settimeofday02.c @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 - * Ported to LTP 07/2001 John George - * Testcase to check that settimeofday() sets errnos correctly. + * Copyright (c) Linux Test Project, 2001-2024 + */ + +/*\ + * Check that settimeofday() sets errnos correctly. */ #include @@ -27,16 +30,7 @@ static void verify_settimeofday(unsigned int n) struct tcase *tc = &tcases[n]; tst_res(TINFO, "%s", tc->message); - TEST(settimeofday(&tc->tv, NULL)); - if (TST_RET != -1) { - tst_res(TFAIL, "settimeofday() succeeded unexpectedly"); - return; - } - - if (TST_ERR != tc->exp_errno) - tst_res(TFAIL | TTERRNO, "Expected %s got ", tst_strerrno(tc->exp_errno)); - else - tst_res(TPASS | TTERRNO, "Received expected errno"); + TST_EXP_FAIL(settimeofday(&tc->tv, NULL), tc->exp_errno); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/setuid/Makefile b/testcases/kernel/syscalls/setuid/Makefile index 1fdd7bd7..88d6385d 100755 --- a/testcases/kernel/syscalls/setuid/Makefile +++ b/testcases/kernel/syscalls/setuid/Makefile @@ -4,8 +4,5 @@ top_srcdir ?= ../../../.. include $(top_srcdir)/include/mk/testcases.mk -#for compat_16.mk uses the compat_16_tst.h -COMPAT_TST_16_H := 1 - include $(abs_srcdir)/../utils/compat_16.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/setuid/setuid01.c b/testcases/kernel/syscalls/setuid/setuid01.c index c136444c..d7b44d79 100755 --- a/testcases/kernel/syscalls/setuid/setuid01.c +++ b/testcases/kernel/syscalls/setuid/setuid01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that setuid(2) returns 0 and effective uid has * been set successfully as a normal or super user. */ diff --git a/testcases/kernel/syscalls/setuid/setuid03.c b/testcases/kernel/syscalls/setuid/setuid03.c index 06934f14..1f96e04f 100755 --- a/testcases/kernel/syscalls/setuid/setuid03.c +++ b/testcases/kernel/syscalls/setuid/setuid03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This test will switch to nobody user for correct error code collection. * Verify setuid returns errno EPERM when it switches to root_user. */ diff --git a/testcases/kernel/syscalls/setxattr/setxattr01.c b/testcases/kernel/syscalls/setxattr/setxattr01.c index 8cd2821d..a405bf42 100755 --- a/testcases/kernel/syscalls/setxattr/setxattr01.c +++ b/testcases/kernel/syscalls/setxattr/setxattr01.c @@ -1,31 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2011 Red Hat, Inc. + * Copyright (c) Linux Test Project, 2011-2024 */ -/* - * Basic tests for setxattr(2) and make sure setxattr(2) handles error +/*\ + * Tests for setxattr(2) and make sure setxattr(2) handles error * conditions correctly. * - * There are 7 test cases: - * 1. Any other flags being set except XATTR_CREATE and XATTR_REPLACE, - * setxattr(2) should return -1 and set errno to EINVAL - * 2. With XATTR_REPLACE flag set but the attribute does not exist, - * setxattr(2) should return -1 and set errno to ENODATA - * 3. Create new attr with name length greater than XATTR_NAME_MAX(255) - * setxattr(2) should return -1 and set errno to ERANGE - * 4. Create new attr whose value length is greater than XATTR_SIZE_MAX(65536) - * setxattr(2) should return -1 and set errno to E2BIG - * 5. Create new attr whose value length is zero, - * setxattr(2) should succeed - * 6. Replace the attr value without XATTR_REPLACE flag being set, - * setxattr(2) should return -1 and set errno to EEXIST - * 7. Replace attr value with XATTR_REPLACE flag being set, - * setxattr(2) should succeed - * 8. Create new attr whose key length is zero, - * setxattr(2) should return -1 and set errno to ERANGE - * 9. Create new attr whose key is NULL, - * setxattr(2) should return -1 and set errno to EFAULT + * - EINVAL - any other flags being set except XATTR_CREATE and XATTR_REPLACE + * - ENODATA - with XATTR_REPLACE flag set but the attribute does not exist + * - ERANGE - create new attr with name length greater than XATTR_NAME_MAX(255) + * - E2BIG - create new attr whose value length is greater than XATTR_SIZE_MAX(65536) + * - SUCCEED - create new attr whose value length is zero + * - EEXIST - replace the attr value without XATTR_REPLACE flag being set + * - SUCCEED - replace attr value with XATTR_REPLACE flag being set + * - ERANGE - create new attr whose key length is zero + * - EFAULT - create new attr whose key is NULL */ #include "config.h" @@ -137,7 +128,7 @@ static void verify_setxattr(unsigned int i) { /* some tests might require existing keys for each iteration */ if (tc[i].keyneeded) { - SAFE_SETXATTR(FNAME, tc[i].key, tc[i].value, tc[i].size, + SAFE_SETXATTR(FNAME, tc[i].key, *tc[i].value, tc[i].size, XATTR_CREATE); } diff --git a/testcases/kernel/syscalls/setxattr/setxattr02.c b/testcases/kernel/syscalls/setxattr/setxattr02.c index 9796f9a5..9f5f998d 100755 --- a/testcases/kernel/syscalls/setxattr/setxattr02.c +++ b/testcases/kernel/syscalls/setxattr/setxattr02.c @@ -1,26 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2011 Red Hat, Inc. + * Copyright (c) Linux Test Project, 2011-2024 */ -/* +/*\ * In the user.* namespace, only regular files and directories can * have extended attributes. Otherwise setxattr(2) will return -1 * and set errno to EPERM. * - * There are 7 test cases: - * 1. Set attribute to a regular file, setxattr(2) should succeed - * 2. Set attribute to a directory, setxattr(2) should succeed - * 3. Set attribute to a symlink which points to the regular file, - * setxattr(2) should return -1 and set errno to EEXIST - * 4. Set attribute to a FIFO, setxattr(2) should return -1 and set - * errno to EPERM - * 5. Set attribute to a char special file, setxattr(2) should - * return -1 and set errno to EPERM - * 6. Set attribute to a block special file, setxattr(2) should - * return -1 and set errno to EPERM - * 7. Set attribute to a UNIX domain socket, setxattr(2) should - * return -1 and set errno to EPERM + * - SUCCEED - set attribute to a regular file + * - SUCCEED - set attribute to a directory + * - EEXIST - set attribute to a symlink which points to the regular file + * - EPERM - set attribute to a FIFO + * - EPERM - set attribute to a char special file + * - EPERM - set attribute to a block special file + * - EPERM - set attribute to a UNIX domain socket */ #include "config.h" diff --git a/testcases/kernel/syscalls/setxattr/setxattr03.c b/testcases/kernel/syscalls/setxattr/setxattr03.c index 58ee0f88..0b5b48a5 100755 --- a/testcases/kernel/syscalls/setxattr/setxattr03.c +++ b/testcases/kernel/syscalls/setxattr/setxattr03.c @@ -1,16 +1,14 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat, Inc. + * Copyright (c) Linux Test Project, 2012-2024 */ -/* +/*\ * setxattr(2) to immutable and append-only files should get EPERM * - * There are 2 test cases: - * 1. Set attribute to a immutable file, setxattr(2) should return -1 - * and set errno to EPERM - * 2. Set attribute to a append-only file, setxattr(2) should return - * -1 and set errno to EPERM + * - Set attribute to a immutable file + * - Set attribute to a append-only file */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/shutdown/.gitignore b/testcases/kernel/syscalls/shutdown/.gitignore new file mode 100644 index 00000000..fd1ed807 --- /dev/null +++ b/testcases/kernel/syscalls/shutdown/.gitignore @@ -0,0 +1,2 @@ +shutdown01 +shutdown02 diff --git a/testcases/kernel/syscalls/shutdown/Makefile b/testcases/kernel/syscalls/shutdown/Makefile new file mode 100644 index 00000000..8cf1b902 --- /dev/null +++ b/testcases/kernel/syscalls/shutdown/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/shutdown/shutdown01.c b/testcases/kernel/syscalls/shutdown/shutdown01.c new file mode 100644 index 00000000..48b1a225 --- /dev/null +++ b/testcases/kernel/syscalls/shutdown/shutdown01.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies the following shutdown() functionalities: + * + * - SHUT_RD should enable send() ops but disable recv() ops + * - SHUT_WR should enable recv() ops but disable send() ops + * - SHUT_RDWR should disable both recv() and send() ops + */ + +#include "tst_test.h" +#include "tst_safe_net.h" + +#define MSGSIZE 4 +#define SOCKETFILE "socket" + +#define OP_DESC(x) .shutdown_op = x, .desc = #x +static struct tcase { + int shutdown_op; + int recv_flag; + int recv_err; + int send_flag; + int send_err; + char *desc; +} tcases[] = { + {OP_DESC(SHUT_RD)}, + {OP_DESC(SHUT_WR), .recv_flag = MSG_DONTWAIT, .recv_err = EWOULDBLOCK, + .send_flag = MSG_NOSIGNAL, .send_err = EPIPE}, + {OP_DESC(SHUT_RDWR), .send_flag = MSG_NOSIGNAL, .send_err = EPIPE} +}; + +static struct sockaddr_un *sock_addr; + +static void run_server(void) +{ + int server_sock; + + server_sock = SAFE_SOCKET(sock_addr->sun_family, SOCK_STREAM, 0); + + SAFE_BIND(server_sock, + (struct sockaddr *)sock_addr, + sizeof(struct sockaddr_un)); + SAFE_LISTEN(server_sock, 10); + + tst_res(TINFO, "Running server on socket file"); + + TST_CHECKPOINT_WAKE_AND_WAIT(0); + + SAFE_CLOSE(server_sock); + SAFE_UNLINK(SOCKETFILE); +} + +static int start_test(void) +{ + int client_sock; + + if (!SAFE_FORK()) { + run_server(); + _exit(0); + } + + TST_CHECKPOINT_WAIT(0); + + tst_res(TINFO, "Connecting to the server"); + + client_sock = SAFE_SOCKET(sock_addr->sun_family, SOCK_STREAM, 0); + SAFE_CONNECT(client_sock, + (struct sockaddr *)sock_addr, + sizeof(struct sockaddr_un)); + + return client_sock; +} + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + int client_sock; + char buff[MSGSIZE] = {0}; + + client_sock = start_test(); + + tst_res(TINFO, "Testing %s flag", tc->desc); + + TST_EXP_PASS(shutdown(client_sock, tc->shutdown_op)); + + if (tc->recv_err) + TST_EXP_FAIL(recv(client_sock, buff, MSGSIZE, tc->recv_flag), tc->recv_err); + else + SAFE_RECV(0, client_sock, buff, MSGSIZE, tc->recv_flag); + + if (tc->send_err) + TST_EXP_FAIL(send(client_sock, buff, MSGSIZE, tc->send_flag), tc->send_err); + else + SAFE_SEND(MSGSIZE, client_sock, buff, MSGSIZE, tc->send_flag); + + SAFE_CLOSE(client_sock); + TST_CHECKPOINT_WAKE(0); +} + +static void setup(void) +{ + sock_addr->sun_family = AF_UNIX; + memcpy(sock_addr->sun_path, SOCKETFILE, sizeof(SOCKETFILE)); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .forks_child = 1, + .needs_checkpoints = 1, + .bufs = (struct tst_buffers []) { + {&sock_addr, .size = sizeof(struct sockaddr_un)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/shutdown/shutdown02.c b/testcases/kernel/syscalls/shutdown/shutdown02.c new file mode 100644 index 00000000..2c488416 --- /dev/null +++ b/testcases/kernel/syscalls/shutdown/shutdown02.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies the following shutdown() errors: + * + * - EBADF sockfd is not a valid file descriptor + * - EINVAL An invalid value was specified in how + * - ENOTCONN The specified socket is not connected + * - ENOTSOCK The file descriptor sockfd does not refer to a socket + */ + +#include "tst_test.h" + +static int file_desc; +static int valid_sock; +static int invalid_sock = -1; + +static struct sockaddr_in *server_addr; + +static struct tcase { + int *socket; + int flags; + int error; +} tcases[] = { + {&invalid_sock, PF_INET, EBADF}, + {&valid_sock, -1, EINVAL}, + {&valid_sock, PF_INET, ENOTCONN}, + {&file_desc, PF_INET, ENOTSOCK}, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + TST_EXP_FAIL(shutdown(*tc->socket, tc->flags), tc->error); +} + +static void setup(void) +{ + file_desc = SAFE_OPEN("notasocket", O_CREAT, 0640); + valid_sock = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0); + + server_addr->sin_family = AF_INET; + server_addr->sin_port = 0; + server_addr->sin_addr.s_addr = INADDR_ANY; + + SAFE_BIND(valid_sock, + (struct sockaddr *)server_addr, + sizeof(struct sockaddr_in)); +} + +static void cleanup(void) +{ + if (valid_sock > 0) + SAFE_CLOSE(valid_sock); + + if (file_desc > 0) + SAFE_CLOSE(file_desc); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .bufs = (struct tst_buffers []) { + {&server_addr, .size = sizeof(struct sockaddr_in)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/sigaltstack/sigaltstack01.c b/testcases/kernel/syscalls/sigaltstack/sigaltstack01.c index 9a2e3a44..14765946 100755 --- a/testcases/kernel/syscalls/sigaltstack/sigaltstack01.c +++ b/testcases/kernel/syscalls/sigaltstack/sigaltstack01.c @@ -142,7 +142,7 @@ int main(int ac, char **av) * Check that main_stk is outside the * alternate stk boundaries. */ - if ((alt_stk < sigstk.ss_sp) && + if ((alt_stk < sigstk.ss_sp) || (alt_stk > (sigstk.ss_sp + SIGSTKSZ))) { tst_resm(TFAIL, "alt. stack is not within the " diff --git a/testcases/kernel/syscalls/sigaltstack/sigaltstack02.c b/testcases/kernel/syscalls/sigaltstack/sigaltstack02.c index c5ab7971..e5412b2d 100755 --- a/testcases/kernel/syscalls/sigaltstack/sigaltstack02.c +++ b/testcases/kernel/syscalls/sigaltstack/sigaltstack02.c @@ -1,191 +1,61 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Test Name: sigalstack02 - * - * Test Description: - * Verify that, - * 1. sigaltstack() fails and sets errno to EINVAL when "ss_flags" field - * pointed to by 'ss' contains invalid flags. - * 2. sigaltstack() fails and sets errno to ENOMEM when the size of alternate - * stack area is less than MINSIGSTKSZ. - * - * Expected Result: - * sigaltstack() should fail with return value -1 and set expected errno. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * if errno set == expected errno - * Issue sys call fails with expected return value and errno. - * Otherwise, - * Issue sys call fails with unexpected errno. - * Otherwise, - * Issue sys call returns unexpected value. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory(s)/file(s) created. - * - * Usage: - * sigaltstack02 [-c n] [-e] [-f] [-i n] [-I x] [-p x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 07/2001 John George - * -Ported - * - * Restrictions: - * This test should be executed by super-user (root) only. + * Copyright (c) International Business Machines Corp., 2001 + * Ported by John George (2001) + * Copyright (C) 2024 SUSE LLC Andrea Manzini */ -#include -#include -#include -#include -#include -#include -#include +/*\ + * Verify that sigaltstack() fails with return value -1 and set expected errno: + * + * - EINVAL on invalid value. + * - ENOMEM on stack is < MINSIGSTKSZ. + */ -#include "test.h" +#include +#include "tst_test.h" #define INVAL_FLAGS 9999 -char *TCID = "sigaltstack02"; -int TST_TOTAL = 2; +static stack_t sigstk; -stack_t sigstk; /* signal stack storing struct. */ - -void setup(void); /* Main setup function of test */ -void cleanup(void); /* cleanup function for the test */ - -struct test_case_t { /* test case struct. to hold diff. test.conds */ +static struct test_case { int flag; int size; char *desc; int exp_errno; -} Test_cases[] = { - { - INVAL_FLAGS, SIGSTKSZ, "Invalid Flag value", EINVAL}, +} tcases[] = { + {INVAL_FLAGS, SIGSTKSZ, "Invalid Flag value", EINVAL}, /* use value low enough for all kernel versions * avoid using MINSIGSTKSZ defined by glibc as it could be different * from the one in kernel ABI */ - { - 0, (2048 - 1), "alternate stack is < MINSIGSTKSZ", ENOMEM}, - { - 0, 0, NULL, 0} -}; + {0, (2048 - 1), "alternate stack is < MINSIGSTKSZ", ENOMEM} }; -int main(int ac, char **av) +static void check_sigaltstack(unsigned int nr) { - int lc; - char *test_desc; /* test specific error message */ - int ind; /* counter to test different test conditions */ - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - for (ind = 0; Test_cases[ind].desc != NULL; ind++) { - sigstk.ss_size = Test_cases[ind].size; - sigstk.ss_flags = Test_cases[ind].flag; - test_desc = Test_cases[ind].desc; - - /* Verify sigaltstack() fails and sets errno */ - TEST(sigaltstack(&sigstk, NULL)); - - /* Check return code from sigaltstack() */ - if (TEST_RETURN == -1) { - if (TEST_ERRNO == - Test_cases[ind].exp_errno) { - tst_resm(TPASS, "stgaltstack() " - "fails, %s, errno:%d", - test_desc, TEST_ERRNO); - } else { - tst_resm(TFAIL, "sigaltstack() " - "fails, %s, errno:%d, " - "expected errno:%d", - test_desc, TEST_ERRNO, - Test_cases - [ind].exp_errno); - } - } else { - tst_resm(TFAIL, "sigaltstack() returned %ld, " - "expected -1, errno:%d", TEST_RETURN, - Test_cases[ind].exp_errno); - } - } - tst_count++; /* incr. TEST_LOOP counter */ - } - - cleanup(); - tst_exit(); + struct test_case *tc = &tcases[nr]; + sigstk.ss_size = tc->size; + sigstk.ss_flags = tc->flag; + TST_EXP_FAIL(sigaltstack(&sigstk, NULL), tc->exp_errno, "%s", tc->desc); } -/* - * void - * setup() - performs all ONE TIME setup for this test. - * Allocate memory for the alternative stack. - */ -void setup(void) +static void setup(void) { - - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - /* Allocate memory for the alternate stack */ - if ((sigstk.ss_sp = malloc(SIGSTKSZ)) == NULL) { - tst_brkm(TFAIL, cleanup, - "could not allocate memory for the alternate stack"); - } + sigstk.ss_sp = SAFE_MALLOC(SIGSTKSZ); } -/* - * void - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Free the memory allocated for alternate stack. - */ -void cleanup(void) +static void cleanup(void) { - free(sigstk.ss_sp); - } + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test = check_sigaltstack, + .tcnt = ARRAY_SIZE(tcases), + .needs_tmpdir = 1 +}; diff --git a/testcases/kernel/syscalls/sighold/sighold02.c b/testcases/kernel/syscalls/sighold/sighold02.c index 1cfb7688..11515558 100755 --- a/testcases/kernel/syscalls/sighold/sighold02.c +++ b/testcases/kernel/syscalls/sighold/sighold02.c @@ -1,19 +1,16 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. - * AUTHOR : Bob Clark - * CO-PILOT : Barrie Kletscher - * DATE STARTED : 9/26/86 + * Authors: Bob Clark, Barrie Kletscher * Copyright (C) 2015 Cyril Hrubis * Copyright (C) 2021 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * This test checks following conditions: - * 1. sighold action to turn off the receipt of all signals was done without error. - * 2. After signals were held, and sent, no signals were trapped. + * + * 1. sighold action to turn off the receipt of all signals was done without error. + * 2. After signals were held, and sent, no signals were trapped. */ #define _XOPEN_SOURCE 600 diff --git a/testcases/kernel/syscalls/signal/signal01.c b/testcases/kernel/syscalls/signal/signal01.c index 27766159..20031af3 100755 --- a/testcases/kernel/syscalls/signal/signal01.c +++ b/testcases/kernel/syscalls/signal/signal01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Test SIGKILL for these items: * 1. SIGKILL can not be set to be ignored, errno:EINVAL (POSIX). * 2. SIGKILL can not be reset to default, errno:EINVAL (POSIX). diff --git a/testcases/kernel/syscalls/signal/signal02.c b/testcases/kernel/syscalls/signal/signal02.c index 54948646..187e6301 100755 --- a/testcases/kernel/syscalls/signal/signal02.c +++ b/testcases/kernel/syscalls/signal/signal02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test that we get an error using illegal signals. */ diff --git a/testcases/kernel/syscalls/signal/signal03.c b/testcases/kernel/syscalls/signal/signal03.c index 8118af4b..339fbbbf 100755 --- a/testcases/kernel/syscalls/signal/signal03.c +++ b/testcases/kernel/syscalls/signal/signal03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Set signals to be ignored. */ diff --git a/testcases/kernel/syscalls/signal/signal04.c b/testcases/kernel/syscalls/signal/signal04.c index 27826ad4..598666c3 100755 --- a/testcases/kernel/syscalls/signal/signal04.c +++ b/testcases/kernel/syscalls/signal/signal04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Restore signals to default behavior. */ diff --git a/testcases/kernel/syscalls/signal/signal05.c b/testcases/kernel/syscalls/signal/signal05.c index f3480f0a..b64f17b5 100755 --- a/testcases/kernel/syscalls/signal/signal05.c +++ b/testcases/kernel/syscalls/signal/signal05.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Set the signal handler to our own function. */ diff --git a/testcases/kernel/syscalls/signalfd/.gitignore b/testcases/kernel/syscalls/signalfd/.gitignore index 3c9ed737..959022f4 100755 --- a/testcases/kernel/syscalls/signalfd/.gitignore +++ b/testcases/kernel/syscalls/signalfd/.gitignore @@ -1 +1,2 @@ /signalfd01 +/signalfd02 diff --git a/testcases/kernel/syscalls/signalfd/signalfd01.c b/testcases/kernel/syscalls/signalfd/signalfd01.c index 8fb16800..47f5eed7 100755 --- a/testcases/kernel/syscalls/signalfd/signalfd01.c +++ b/testcases/kernel/syscalls/signalfd/signalfd01.c @@ -1,302 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) Red Hat Inc., 2008 - * - * 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 + * Copyright (c) Red Hat Inc., 2008 + * Copyright (c) Linux Test Project, 2006-2024 */ -/* - * NAME - * signalfd01.c +/*\ + * Verify that signalfd() works as expected. * - * DESCRIPTION - * Check signalfd can receive signals - * - * USAGE - * signalfd01 - * - * HISTORY - * 9/2008 Initial version by Masatake YAMATO - * - * RESTRICTIONS - * None + * - signalfd() can create fd, and fd can receive signal. + * - signalfd() can reassign fd, and fd can receive signal. */ -#define _GNU_SOURCE -#include "config.h" - -#include "test.h" - -#include -#include -#include -#include -#include -#include "ltp_signal.h" - -TCID_DEFINE(signalfd01); -int TST_TOTAL = 1; - -#ifndef HAVE_SIGNALFD -#define USE_STUB -#endif - -#if defined HAVE_SYS_SIGNALFD_H #include -#elif defined HAVE_LINUX_SIGNALFD_H -#include -#define USE_OWNIMPL -#else -#define USE_STUB -#endif +#include "tst_test.h" -#ifndef HAVE_STRUCT_SIGNALFD_SIGINFO_SSI_SIGNO -#define USE_STUB -#endif +static int fd_signal = -1; +static sigset_t mask1; +static sigset_t mask2; -#ifdef USE_STUB -int main(void) +static void check_signal(int fd, uint32_t signal) { - tst_brkm(TCONF, NULL, "System doesn't support execution of the test"); + pid_t pid = getpid(); + ssize_t bytes; + struct signalfd_siginfo siginfo; + + SAFE_KILL(pid, signal); + bytes = SAFE_READ(0, fd, &siginfo, sizeof(siginfo)); + TST_EXP_EQ_LI(bytes, sizeof(siginfo)); + TST_EXP_EQ_LI(siginfo.ssi_signo, signal); } -#else -#if defined USE_OWNIMPL -#include "lapi/syscalls.h" -int signalfd(int fd, const sigset_t * mask, int flags) +static void setup(void) { - /* Taken from GLIBC. */ - return tst_syscall(__NR_signalfd, fd, mask, SIGSETSIZE); -} -#endif - -void cleanup(void); -void setup(void); - -int do_test1(uint32_t sig) -{ - int sfd_for_next; - int sfd; - sigset_t mask; - pid_t pid; - struct signalfd_siginfo fdsi; - ssize_t s; - - sigemptyset(&mask); - sigaddset(&mask, sig); - if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { - tst_brkm(TBROK, cleanup, - "sigprocmask() Failed: errno=%d : %s", - errno, strerror(errno)); - } - - TEST(signalfd(-1, &mask, 0)); - - if ((sfd = TEST_RETURN) == -1) { - tst_resm(TFAIL, - "signalfd() Failed, errno=%d : %s", - TEST_ERRNO, strerror(TEST_ERRNO)); - sfd_for_next = -1; - return sfd_for_next; - - } else { - tst_resm(TPASS, "signalfd is created successfully"); - sfd_for_next = sfd; - goto out; - } - - if (fcntl(sfd, F_SETFL, O_NONBLOCK) == -1) { - close(sfd); - tst_brkm(TBROK, cleanup, - "setting signalfd nonblocking mode failed: errno=%d : %s", - errno, strerror(errno)); - } - - pid = getpid(); - if (kill(pid, sig) == -1) { - close(sfd); - tst_brkm(TBROK, cleanup, - "kill(self, %s) failed: errno=%d : %s", - strsignal(sig), errno, strerror(errno)); - } - - s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); - if ((s > 0) && (s != sizeof(struct signalfd_siginfo))) { - tst_resm(TFAIL, - "getting incomplete signalfd_siginfo data: " - "actual-size=%zd, expected-size=%zu", - s, sizeof(struct signalfd_siginfo)); - sfd_for_next = -1; - close(sfd); - goto out; - } else if (s < 0) { - if (errno == EAGAIN) { - tst_resm(TFAIL, - "signalfd_siginfo data is not delivered yet"); - sfd_for_next = -1; - close(sfd); - goto out; - } else { - close(sfd); - tst_brkm(TBROK, cleanup, - "read signalfd_siginfo data failed: errno=%d : %s", - errno, strerror(errno)); - } - } else if (s == 0) { - tst_resm(TFAIL, "got EOF unexpectedly"); - sfd_for_next = -1; - close(sfd); - goto out; - } - - if (fdsi.ssi_signo == sig) { - tst_resm(TPASS, "got expected signal"); - sfd_for_next = sfd; - goto out; - } else { - tst_resm(TFAIL, "got unexpected signal: signal=%d : %s", - fdsi.ssi_signo, strsignal(fdsi.ssi_signo)); - sfd_for_next = -1; - close(sfd); - goto out; - } - -out: - return sfd_for_next; + SAFE_SIGEMPTYSET(&mask1); + SAFE_SIGADDSET(&mask1, SIGUSR1); + SAFE_SIGPROCMASK(SIG_BLOCK, &mask1, NULL); + SAFE_SIGEMPTYSET(&mask2); + SAFE_SIGADDSET(&mask2, SIGUSR2); + SAFE_SIGPROCMASK(SIG_BLOCK, &mask2, NULL); } -void do_test2(int fd, uint32_t sig) +static void cleanup(void) { - int sfd; - sigset_t mask; - pid_t pid; - struct signalfd_siginfo fdsi; - ssize_t s; + if (fd_signal > 0) + SAFE_CLOSE(fd_signal); +} - sigemptyset(&mask); - sigaddset(&mask, sig); - if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { - close(fd); - tst_brkm(TBROK, cleanup, - "sigprocmask() Failed: errno=%d : %s", - errno, strerror(errno)); - } - - TEST(signalfd(fd, &mask, 0)); - - if ((sfd = TEST_RETURN) == -1) { - tst_resm(TFAIL, - "reassignment the file descriptor by signalfd() failed, errno=%d : %s", - TEST_ERRNO, strerror(TEST_ERRNO)); +static void verify_signalfd(void) +{ + /* create fd */ + TST_EXP_FD(signalfd(fd_signal, &mask1, 0), + "%s", "signalfd() create fd"); + if (TST_RET == -1) return; - } else if (sfd != fd) { - tst_resm(TFAIL, - "different fd is returned in reassignment: expected-fd=%d, actual-fd=%d", - fd, sfd); - close(sfd); + fd_signal = TST_RET; + check_signal(fd_signal, SIGUSR1); + /* reassign fd */ + TST_EXP_FD(signalfd(fd_signal, &mask2, 0), "%s", + "signalfd() reassign fd"); + if (TST_RET == -1) return; - - } else { - tst_resm(TPASS, "signalfd is successfully reassigned"); - goto out; - } - - pid = getpid(); - if (kill(pid, sig) == -1) { - close(sfd); - tst_brkm(TBROK, cleanup, - "kill(self, %s) failed: errno=%d : %s", - strsignal(sig), errno, strerror(errno)); - } - - s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); - if ((s > 0) && (s != sizeof(struct signalfd_siginfo))) { - tst_resm(TFAIL, - "getting incomplete signalfd_siginfo data: " - "actual-size=%zd, expected-size= %zu", - s, sizeof(struct signalfd_siginfo)); - goto out; - } else if (s < 0) { - if (errno == EAGAIN) { - tst_resm(TFAIL, - "signalfd_siginfo data is not delivered yet"); - goto out; - } else { - close(sfd); - tst_brkm(TBROK, cleanup, - "read signalfd_siginfo data failed: errno=%d : %s", - errno, strerror(errno)); - } - } else if (s == 0) { - tst_resm(TFAIL, "got EOF unexpectedly"); - goto out; - } - - if (fdsi.ssi_signo == sig) { - tst_resm(TPASS, "got expected signal"); - goto out; - } else { - tst_resm(TFAIL, "got unexpected signal: signal=%d : %s", - fdsi.ssi_signo, strsignal(fdsi.ssi_signo)); - goto out; - } - -out: - return; + TST_EXP_EQ_LI(TST_RET, fd_signal); + check_signal(fd_signal, SIGUSR2); } -int main(int argc, char **argv) -{ - int lc; - int sfd; - - tst_parse_opts(argc, argv, NULL, NULL); - - setup(); - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - sfd = do_test1(SIGUSR1); - if (sfd < 0) - continue; - - do_test2(sfd, SIGUSR2); - close(sfd); - } - - cleanup(); - - tst_exit(); -} - -/* - * setup() - performs all the ONE TIME setup for this test. - */ -void setup(void) -{ - - TEST_PAUSE; -} - -/* - * cleanup() - performs all the ONE TIME cleanup for this test at completion - * or premature exit. - */ -void cleanup(void) -{ - -} - -#endif +static struct tst_test test = { + .test_all = verify_signalfd, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/signalfd/signalfd02.c b/testcases/kernel/syscalls/signalfd/signalfd02.c new file mode 100644 index 00000000..332b5f59 --- /dev/null +++ b/testcases/kernel/syscalls/signalfd/signalfd02.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Author: Ma Xinjian + */ + +/*\ + * Verify that signalfd(2) fails with: + * + * - EBADF when fd is invalid + * - EINVAL when fd is not a valid signalfd file descriptor + * - EINVAL when flags are invalid + */ + +#include +#include "tst_test.h" + +#define SIGNAL_FILE "signal_file" + +static int fd_ebadf = -2; +static int fd_einval1; +static int fd_einval2 = -1; + +static sigset_t *mask; + +static struct test_case_t { + int *fd; + int flags; + int expected_errno; + char *desc; +} tcases[] = { + {&fd_ebadf, 0, EBADF, "fd is invalid"}, + {&fd_einval1, 0, EINVAL, + "fd is not a valid signalfd file descriptor"}, + {&fd_einval2, -1, EINVAL, "flags are invalid"}, +}; + +static void setup(void) +{ + SAFE_SIGEMPTYSET(mask); + SAFE_SIGADDSET(mask, SIGUSR1); + SAFE_SIGPROCMASK(SIG_BLOCK, mask, NULL); + + fd_einval1 = SAFE_OPEN(SIGNAL_FILE, O_CREAT, 0777); +} + +static void cleanup(void) +{ + if (fd_einval1 > 0) + SAFE_CLOSE(fd_einval1); +} + +static void verify_signalfd(unsigned int i) +{ + struct test_case_t *tc = &tcases[i]; + + TST_EXP_FAIL2(signalfd(*(tc->fd), mask, tc->flags), + tc->expected_errno, "%s", tc->desc); +} + +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .test = verify_signalfd, + .setup = setup, + .cleanup = cleanup, + .needs_tmpdir = 1, + .bufs = (struct tst_buffers []) { + {&mask, .size = sizeof(sigset_t)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/sigpending/sigpending02.c b/testcases/kernel/syscalls/sigpending/sigpending02.c index b9a3c5e8..d901540c 100755 --- a/testcases/kernel/syscalls/sigpending/sigpending02.c +++ b/testcases/kernel/syscalls/sigpending/sigpending02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2002 * Copyright (c) Linux Test Project, 2009-2019 diff --git a/testcases/kernel/syscalls/sigrelse/sigrelse01.c b/testcases/kernel/syscalls/sigrelse/sigrelse01.c index 38086caa..23c67582 100755 --- a/testcases/kernel/syscalls/sigrelse/sigrelse01.c +++ b/testcases/kernel/syscalls/sigrelse/sigrelse01.c @@ -192,9 +192,6 @@ int main(int argc, char **argv) * parse standard options */ tst_parse_opts(argc, argv, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&child, "dd", &pipe_fd[1], &pipe_fd2[0]); -#endif /* * perform global setup for test @@ -208,23 +205,12 @@ int main(int argc, char **argv) /* * fork off a child process */ - if ((pid = FORK_OR_VFORK()) < 0) { + if ((pid = tst_fork()) < 0) tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); - - } else if (pid > 0) { + else if (pid > 0) parent(); - - } else { -#ifdef UCLINUX - if (self_exec(argv[0], "dd", pipe_fd[1], pipe_fd2[0]) < - 0) { - tst_brkm(TBROK | TERRNO, cleanup, - "self_exec() failed"); - } -#else + else child(); -#endif - } } @@ -500,12 +486,13 @@ static void child(void) * then PASS, otherwise FAIL. */ - if (exit_val == EXIT_OK) { - (void)memcpy(note, (char *)sig_array, sizeof(sig_array)); - } - /* send note to parent and exit */ - if (write_pipe(pipe_fd[1], note) < 0) { + if (exit_val == EXIT_OK) { + if (write(pipe_fd[1], sig_array, sizeof(sig_array)) < 0) { + tst_resm(TBROK | TERRNO, "write() pipe failed"); + exit(WRITE_BROK); + } + } else if (write_pipe(pipe_fd[1], note) < 0) { /* * write_pipe() failed. Set exit value to WRITE_BROK to let * parent know what happened @@ -636,7 +623,7 @@ static int write_pipe(int fd, char *msg) printf("write_pipe: pid=%d, sending %s.\n", getpid(), msg); #endif - if (write(fd, msg, MAXMESG) < 0) { + if (write(fd, msg, strlen(msg) + 1) < 0) { (void)sprintf(mesg, "write() pipe failed. error:%d %s.", errno, strerror(errno)); @@ -752,8 +739,10 @@ int choose_sig(int sig) } - return 1; + if (sig < 32) + return 1; + return sig >= SIGRTMIN && sig <= SIGRTMAX; } void setup(void) diff --git a/testcases/kernel/syscalls/sigsuspend/.gitignore b/testcases/kernel/syscalls/sigsuspend/.gitignore index 46ea52f7..926b7357 100755 --- a/testcases/kernel/syscalls/sigsuspend/.gitignore +++ b/testcases/kernel/syscalls/sigsuspend/.gitignore @@ -1 +1,2 @@ /sigsuspend01 +/sigsuspend02 diff --git a/testcases/kernel/syscalls/sigsuspend/sigsuspend01.c b/testcases/kernel/syscalls/sigsuspend/sigsuspend01.c index 2276132e..66307f80 100755 --- a/testcases/kernel/syscalls/sigsuspend/sigsuspend01.c +++ b/testcases/kernel/syscalls/sigsuspend/sigsuspend01.c @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) Linux Test Project, 2003-2024 + */ + +/*\ + * Verify the basic sigsuspend(2) syscall functionality: * - * Description: - * Verify the basic function of sigsuspend(): - * 1) sigsuspend() can replace process's current signal mask - * by the specified signal mask and suspend the process - * execution until the delivery of a signal. - * 2) sigsuspend() should return after the execution of signal - * handler and restore the previous signal mask. + * - sigsuspend(2) can replace process's current signal mask by the specified + * signal mask and suspend the process execution until the delivery of a + * signal. + * - sigsuspend(2) should return after the execution of signal handler and + * restore the previous signal mask. */ #include diff --git a/testcases/kernel/syscalls/sigsuspend/sigsuspend02.c b/testcases/kernel/syscalls/sigsuspend/sigsuspend02.c new file mode 100644 index 00000000..d809bacc --- /dev/null +++ b/testcases/kernel/syscalls/sigsuspend/sigsuspend02.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Copyright (c) Linux Test Project, 2024 + * Author: Ma Xinjian + */ + +/*\ + * Verify that sigsuspend(2) fails with + * + * - EFAULT mask points to memory which is not a valid part of the + * process address space. + */ + +#include "tst_test.h" + +static void *invalid_mask; + +static void setup(void) +{ + invalid_mask = tst_get_bad_addr(NULL); +} + +static void verify_sigsuspend(void) +{ + TST_EXP_FAIL(sigsuspend(invalid_mask), EFAULT); +} + +static struct tst_test test = { + .test_all = verify_sigsuspend, + .setup = setup, +}; diff --git a/testcases/kernel/syscalls/sigtimedwait/Makefile b/testcases/kernel/syscalls/sigtimedwait/Makefile index 1ae50b32..f96d5dc3 100755 --- a/testcases/kernel/syscalls/sigtimedwait/Makefile +++ b/testcases/kernel/syscalls/sigtimedwait/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpsigwait +LTPLIBS = sigwait include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/sigwait/Makefile b/testcases/kernel/syscalls/sigwait/Makefile index 1ae50b32..f96d5dc3 100755 --- a/testcases/kernel/syscalls/sigwait/Makefile +++ b/testcases/kernel/syscalls/sigwait/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpsigwait +LTPLIBS = sigwait include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/sigwaitinfo/Makefile b/testcases/kernel/syscalls/sigwaitinfo/Makefile index 1ae50b32..f96d5dc3 100755 --- a/testcases/kernel/syscalls/sigwaitinfo/Makefile +++ b/testcases/kernel/syscalls/sigwaitinfo/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpsigwait +LTPLIBS = sigwait include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/socket/socket01.c b/testcases/kernel/syscalls/socket/socket01.c index 4724609d..9563e19f 100755 --- a/testcases/kernel/syscalls/socket/socket01.c +++ b/testcases/kernel/syscalls/socket/socket01.c @@ -1,15 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 +* Copyright (c) Linux Test Project, 2003-2024 */ -/* -* Test Name: socket01 -* -* Test Description: -* Verify that socket() returns the proper errno for various failure cases -* -*/ +/*\ + * Test creating TCP, UDP, and Unix doman dgram sockets with socket() syscall. + * + * Also verify that socket() fails and set proper errno + * + * - EAFNOSUPPORT on invalid domain + * - EINVAL on invalid type + * - EPROTONOSUPPORT on raw open as non-root + * - EPROTONOSUPPORT on UDP stream + * - EPROTONOSUPPORT on TCP dgram + * - EPROTONOSUPPORT on ICMP stream + */ #include #include diff --git a/testcases/kernel/syscalls/socket/socket02.c b/testcases/kernel/syscalls/socket/socket02.c index 51b8cc59..30f76908 100755 --- a/testcases/kernel/syscalls/socket/socket02.c +++ b/testcases/kernel/syscalls/socket/socket02.c @@ -2,15 +2,12 @@ /* * Copyright (c) Ulrich Drepper * Copyright (c) International Business Machines Corp., 2009 +* Copyright (c) Linux Test Project, 2010-2022 */ -/* -* Test Name: socket02 -* -* Description: -* This program tests the new flag SOCK_CLOEXEC and SOCK_NONBLOCK introduced -* in socket() in kernel 2.6.27. -*/ +/*\ + * Test socket() with SOCK_CLOEXEC and SOCK_NONBLOCK flags. + */ #include #include diff --git a/testcases/kernel/syscalls/socketcall/socketcall01.c b/testcases/kernel/syscalls/socketcall/socketcall01.c index 20530637..55d1e165 100755 --- a/testcases/kernel/syscalls/socketcall/socketcall01.c +++ b/testcases/kernel/syscalls/socketcall/socketcall01.c @@ -4,8 +4,12 @@ * Copyright (c) 2016 Cyril Hrubis * Copyright (c) Linux Test Project, 2017-2020 * Author: Sowmya Adiga + */ + +/*\ + * Basic test for the socketcall(2) raw syscall. * - * This is a basic test for the socketcall(2) system call. + * Test creating TCP, UDP, raw socket and unix domain dgram. */ #include diff --git a/testcases/kernel/syscalls/socketpair/socketpair01.c b/testcases/kernel/syscalls/socketpair/socketpair01.c index 675eb535..64634853 100755 --- a/testcases/kernel/syscalls/socketpair/socketpair01.c +++ b/testcases/kernel/syscalls/socketpair/socketpair01.c @@ -1,14 +1,24 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 +* Copyright (c) Linux Test Project, 2003-2022 */ -/* -* Test Name: socketpair01 -* -* Test Description: -* Verify that socketpair() returns the proper errno for various failure cases -*/ +/*\ + * Verify that socketpair(2) fails and set proper errno + * + * - EAFNOSUPPORT on invalid domain + * - EINVAL on invalid type + * - EPROTONOSUPPORT on raw open as non-root + * - EFAULT on bad aligned pointer + * - EFAULT on bad unaligned pointer + * - EOPNOTSUPP on UDP socket + * - EPROTONOSUPPORT on TCP dgram + * - EOPNOTSUPP on TCP socket + * - EPROTONOSUPPORT on ICMP stream + * + * Also test creating UNIX domain dgram. + */ #include #include @@ -34,10 +44,8 @@ struct test_case_t { {PF_INET, 75, 0, fds, -1, EINVAL, "invalid type"}, {PF_UNIX, SOCK_DGRAM, 0, fds, 0, 0, "UNIX domain dgram"}, {PF_INET, SOCK_RAW, 0, fds, -1, EPROTONOSUPPORT, "raw open as non-root"}, -#ifndef UCLINUX {PF_UNIX, SOCK_STREAM, 0, 0, -1, EFAULT, "bad aligned pointer"}, {PF_UNIX, SOCK_STREAM, 0, (int *)7, -1, EFAULT, "bad unaligned pointer"}, -#endif {PF_INET, SOCK_DGRAM, 17, fds, -1, EOPNOTSUPP, "UDP socket"}, {PF_INET, SOCK_DGRAM, 6, fds, -1, EPROTONOSUPPORT, "TCP dgram"}, {PF_INET, SOCK_STREAM, 6, fds, -1, EOPNOTSUPP, "TCP socket"}, diff --git a/testcases/kernel/syscalls/socketpair/socketpair02.c b/testcases/kernel/syscalls/socketpair/socketpair02.c index eb679d5d..1a6f1331 100755 --- a/testcases/kernel/syscalls/socketpair/socketpair02.c +++ b/testcases/kernel/syscalls/socketpair/socketpair02.c @@ -2,15 +2,12 @@ /* * Copyright (c) Ulrich Drepper * Copyright (c) International Business Machines Corp., 2009 +* Copyright (c) Linux Test Project, 2010-2022 */ -/* -* Test Name: socketpair02 -* -* Description: -* This Program tests the new flag SOCK_CLOEXEC and SOCK_NONBLOCK introduced -* in socketpair() in kernel 2.6.27. -*/ +/*\ + * Test socket() with SOCK_CLOEXEC and SOCK_NONBLOCK flags. + */ #include #include diff --git a/testcases/kernel/syscalls/sockioctl/sockioctl01.c b/testcases/kernel/syscalls/sockioctl/sockioctl01.c index ff3738f3..e81ec20a 100755 --- a/testcases/kernel/syscalls/sockioctl/sockioctl01.c +++ b/testcases/kernel/syscalls/sockioctl/sockioctl01.c @@ -87,13 +87,11 @@ struct test_case_t { (struct sockaddr *)&fsin1, sizeof(fsin1), -1, EINVAL, setup0, cleanup0, "not a socket"} , -#if !defined(UCLINUX) { PF_INET, SOCK_STREAM, 0, SIOCATMARK, 0, (struct sockaddr *)&fsin1, sizeof(fsin1), -1, EFAULT, setup1, cleanup1, "invalid option buffer"} , -#endif { PF_INET, SOCK_DGRAM, 0, SIOCATMARK, &optval, (struct sockaddr *)&fsin1, sizeof(fsin1), -1, diff --git a/testcases/kernel/syscalls/splice/.gitignore b/testcases/kernel/syscalls/splice/.gitignore index a3e502f0..96b1727a 100755 --- a/testcases/kernel/syscalls/splice/.gitignore +++ b/testcases/kernel/syscalls/splice/.gitignore @@ -3,3 +3,7 @@ /splice03 /splice04 /splice05 +/splice06 +/splice07 +/splice08 +/splice09 diff --git a/testcases/kernel/syscalls/splice/splice02.c b/testcases/kernel/syscalls/splice/splice02.c index 1da1186b..53d243db 100755 --- a/testcases/kernel/syscalls/splice/splice02.c +++ b/testcases/kernel/syscalls/splice/splice02.c @@ -5,7 +5,6 @@ */ /*\ - * [Description] * Original reproducer for kernel fix * bf40d3435caf NFS: add support for splice writes * from v2.6.31-rc1. @@ -13,6 +12,7 @@ * http://lkml.org/lkml/2009/4/2/55 * * [ALGORITHM] + * * - create pipe * - fork(), child replace stdin with pipe * - parent write to pipe diff --git a/testcases/kernel/syscalls/splice/splice03.c b/testcases/kernel/syscalls/splice/splice03.c index c054e6c1..32a683dc 100755 --- a/testcases/kernel/syscalls/splice/splice03.c +++ b/testcases/kernel/syscalls/splice/splice03.c @@ -3,23 +3,17 @@ * Copyright (c) 2014 Fujitsu Ltd. * Author: Xing Gu */ -/* - * Description: - * Verify that, - * 1) splice() returns -1 and sets errno to EBADF if the file - * descriptor fd_in is not valid. - * 2) splice() returns -1 and sets errno to EBADF if the file - * descriptor fd_out is not valid. - * 3) splice() returns -1 and sets errno to EBADF if the file - * descriptor fd_in does not have proper read-write mode. - * 4) splice() returns -1 and sets errno to EINVAL if target - * file is opened in append mode. - * 5) splice() returns -1 and sets errno to EINVAL if neither - * of the descriptors refer to a pipe. - * 6) splice() returns -1 and sets errno to ESPIPE if off_in is - * not NULL when the file descriptor fd_in refers to a pipe. - * 7) splice() returns -1 and sets errno to ESPIPE if off_out is - * not NULL when the file descriptor fd_out refers to a pipe. + +/*\ + * Verify that, splice(2) returns -1 and sets errno to + * + * 1. EBADF if the file descriptor fd_in is not valid + * 2. EBADF if the file descriptor fd_out is not valid + * 3. EBADF if the file descriptor fd_in does not have proper read-write mode + * 4. EINVAL if target file is opened in append mode + * 5. EINVAL if neither of the descriptors refer to a pipe + * 6. ESPIPE if off_in is not NULL when the file descriptor fd_in refers to a pipe + * 7. ESPIPE if off_out is not NULL when the file descriptor fd_out refers to a pipe */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/splice/splice06.c b/testcases/kernel/syscalls/splice/splice06.c new file mode 100644 index 00000000..e64d32de --- /dev/null +++ b/testcases/kernel/syscalls/splice/splice06.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 SUSE LLC + */ + +/*\ + * This test is cover splice() on proc files. + * + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" +#include "lapi/splice.h" + +#define BUF_SIZE 100 +#define PIPE_MAX_INIT_SIZE 65536 +#define DOMAIN_INIT_NAME "LTP_INIT" +#define DOMAIN_TEST_NAME "LTP_TEST" +#define INTEGER_PROCFILE "/proc/sys/fs/pipe-max-size" +#define STRING_PROCFILE "/proc/sys/kernel/domainname" +static int pipe_max_test_size; + +static void format_str(char *str) +{ + int i; + + for (i = 0; i < BUF_SIZE ; i++) { + if (!isdigit(str[i])) { + str[i] = '\0'; + break; + } + } + if (i == BUF_SIZE) + tst_brk(TBROK, "can not find nonnumeric character from input string"); +} + +static int splice_read_num(const char *file) +{ + int pipes[2]; + int fd_in; + int ret; + int num; + char buf[BUF_SIZE]; + + memset(buf, '\0', sizeof(buf)); + fd_in = SAFE_OPEN(file, O_RDONLY); + SAFE_PIPE(pipes); + + ret = splice(fd_in, NULL, pipes[1], NULL, BUF_SIZE - 1, 0); + if (ret < 0) + tst_brk(TBROK | TERRNO, "splice(fd_in, pipe) failed"); + + SAFE_READ(0, pipes[0], buf, BUF_SIZE); + + /* Search for the first nonnumeric character and replace it with \0 */ + format_str(buf); + + if (tst_parse_int(buf, &num, 0, INT_MAX)) + tst_brk(TBROK, "Invalid buffer num %s", buf); + + SAFE_CLOSE(fd_in); + SAFE_CLOSE(pipes[0]); + SAFE_CLOSE(pipes[1]); + + return num; +} + +static char *splice_read_str(const char *file, char *dest) +{ + int pipes[2]; + int fd_in; + int ret; + + fd_in = SAFE_OPEN(file, O_RDONLY); + SAFE_PIPE(pipes); + + ret = splice(fd_in, NULL, pipes[1], NULL, BUF_SIZE, 0); + if (ret < 0) + tst_brk(TBROK | TERRNO, "splice(fd_in, pipe) failed"); + + SAFE_READ(0, pipes[0], dest, BUF_SIZE); + + SAFE_CLOSE(fd_in); + SAFE_CLOSE(pipes[0]); + SAFE_CLOSE(pipes[1]); + + return dest; +} + + +static void splice_write_num(const char *file, int num) +{ + int pipes[2]; + int fd_out; + int ret; + char buf[BUF_SIZE]; + + memset(buf, '\0', sizeof(buf)); + + fd_out = SAFE_OPEN(file, O_WRONLY, 0777); + SAFE_PIPE(pipes); + sprintf(buf, "%d", num); + + SAFE_WRITE(SAFE_WRITE_ALL, pipes[1], buf, strlen(buf)); + + ret = splice(pipes[0], NULL, fd_out, NULL, BUF_SIZE, 0); + if (ret < 0) + tst_brk(TBROK | TERRNO, "splice write failed"); + + SAFE_CLOSE(fd_out); + SAFE_CLOSE(pipes[0]); + SAFE_CLOSE(pipes[1]); +} + +static void splice_write_str(const char *file, char *dest) +{ + int pipes[2]; + int fd_out; + int ret; + + fd_out = SAFE_OPEN(file, O_WRONLY, 0777); + SAFE_PIPE(pipes); + + SAFE_WRITE(SAFE_WRITE_ALL, pipes[1], dest, strlen(dest)); + + ret = splice(pipes[0], NULL, fd_out, NULL, BUF_SIZE, 0); + if (ret < 0) + tst_brk(TBROK | TERRNO, "splice write failed"); + + SAFE_CLOSE(fd_out); + SAFE_CLOSE(pipes[0]); + SAFE_CLOSE(pipes[1]); +} + +static void file_write_num(const char *file, int num) +{ + SAFE_FILE_PRINTF(file, "%d", num); +} + +static void file_write_str(const char *file, char *dest) +{ + SAFE_FILE_PRINTF(file, "%s", dest); +} + +static int file_read_num(const char *file) +{ + int num; + + SAFE_FILE_SCANF(file, "%d", &num); + + return num; +} + +static char *file_read_str(const char *file, char *dest) +{ + SAFE_FILE_SCANF(file, "%s", dest); + return dest; +} + +static void splice_test(void) +{ + + char buf_file[BUF_SIZE]; + char buf_splice[BUF_SIZE]; + + if (file_read_num(INTEGER_PROCFILE) == splice_read_num(INTEGER_PROCFILE)) + tst_res(TPASS, "Read num through splice correctly"); + else + tst_brk(TBROK | TERRNO, "Read num through splice failed"); + + splice_write_num(INTEGER_PROCFILE, pipe_max_test_size); + + if (file_read_num(INTEGER_PROCFILE) == pipe_max_test_size) + tst_res(TPASS, "Write num through splice correctly"); + else + tst_brk(TBROK | TERRNO, "Write num through splice failed"); + + memset(buf_file, '\0', sizeof(buf_file)); + memset(buf_splice, '\0', sizeof(buf_splice)); + + file_read_str(STRING_PROCFILE, buf_file); + splice_read_str(STRING_PROCFILE, buf_splice); + + if (!strncmp(buf_file, buf_splice, strlen(buf_file))) + tst_res(TPASS, "Read string through splice correctly"); + else + tst_brk(TBROK | TERRNO, "Read string through splice failed"); + + memset(buf_file, '\0', sizeof(buf_file)); + + splice_write_str(STRING_PROCFILE, DOMAIN_TEST_NAME); + file_read_str(STRING_PROCFILE, buf_file); + + if (!strncmp(buf_file, DOMAIN_TEST_NAME, strlen(buf_file))) + tst_res(TPASS, "Write string through splice correctly"); + else + tst_brk(TBROK | TERRNO, "Write string through splice failed"); +} + +static void setup(void) +{ + pipe_max_test_size = getpagesize(); + file_write_str(STRING_PROCFILE, DOMAIN_INIT_NAME); + file_write_num(STRING_PROCFILE, PIPE_MAX_INIT_SIZE); +} + +static struct tst_test test = { + .min_kver = "5.11", + .setup = setup, + .test_all = splice_test, + .needs_tmpdir = 1, + .save_restore = (const struct tst_path_val[]) { + {INTEGER_PROCFILE, NULL, TST_SR_TCONF}, + {STRING_PROCFILE, NULL, TST_SR_TCONF}, + {} + }, +}; diff --git a/testcases/kernel/syscalls/splice/splice07.c b/testcases/kernel/syscalls/splice/splice07.c new file mode 100644 index 00000000..c750a14b --- /dev/null +++ b/testcases/kernel/syscalls/splice/splice07.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2023-2024 Cyril Hrubis + */ + +/*\ + * Iterate over all kinds of file descriptors and feed splice() with all possible + * combinations where at least one file descriptor is invalid. We do expect the + * syscall to fail either with EINVAL or EBADF. + */ + +#define _GNU_SOURCE + +#include +#include + +#include "tst_test.h" + +static void check_splice(struct tst_fd *fd_in, struct tst_fd *fd_out) +{ + /* These combinations just hang since the pipe is empty */ + if (fd_in->type == TST_FD_PIPE_READ) { + switch (fd_out->type) { + case TST_FD_FILE: + case TST_FD_PIPE_WRITE: + case TST_FD_UNIX_SOCK: + case TST_FD_INET_SOCK: + case TST_FD_MEMFD: + case TST_FD_MEMFD_SECRET: + return; + default: + break; + } + } + + if (fd_out->type == TST_FD_PIPE_WRITE) { + switch (fd_in->type) { + /* While these combinations succeeed */ + case TST_FD_DEV_ZERO: + case TST_FD_FILE: + case TST_FD_PROC_MAPS: + case TST_FD_MEMFD: + case TST_FD_MEMFD_SECRET: + return; + /* And this complains about socket not being connected */ + case TST_FD_INET_SOCK: + return; + default: + break; + } + } + + const int exp_errnos[] = {EBADF, EINVAL}; + + TST_EXP_FAIL2_ARR(splice(fd_in->fd, NULL, fd_out->fd, NULL, 1, 0), + exp_errnos, ARRAY_SIZE(exp_errnos), "splice() on %s -> %s", + tst_fd_desc(fd_in), tst_fd_desc(fd_out)); +} + +static void verify_splice(void) +{ + TST_FD_FOREACH(fd_in) { + tst_res(TINFO, "%s -> ...", tst_fd_desc(&fd_in)); + TST_FD_FOREACH(fd_out) + check_splice(&fd_in, &fd_out); + } +} + +static struct tst_test test = { + .test_all = verify_splice, +}; diff --git a/testcases/kernel/syscalls/splice/splice08.c b/testcases/kernel/syscalls/splice/splice08.c new file mode 100644 index 00000000..3f9a070b --- /dev/null +++ b/testcases/kernel/syscalls/splice/splice08.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Cyril Hrubis + */ + +/*\ + * Test for splicing from /dev/zero and /dev/full. + * + * The support for splicing from /dev/zero and /dev/full was removed in: + * c6585011bc1d ("splice: Remove generic_file_splice_read()") + * + * And added back in: + * 1b057bd800c3 ("drivers/char/mem: implement splice() for /dev/zero, /dev/full") + */ + +#define _GNU_SOURCE +#include "tst_test.h" + +static int fd_zero; +static int fd_full; + +static void test_splice(unsigned int bytes, int dev_fd) +{ + int pipefd[2]; + char buf[bytes]; + size_t i; + int fail = 0; + + memset(buf, 0xff, sizeof(buf)); + + SAFE_PIPE(pipefd); + + TST_EXP_POSITIVE(splice(dev_fd, NULL, pipefd[1], NULL, sizeof(buf), 0)); + + if (!TST_PASS) + goto ret; + + if ((size_t)TST_RET != sizeof(buf)) { + tst_res(TFAIL, "Only part %lu bytes of %u were spliced", + TST_RET, bytes); + goto ret; + } + + SAFE_READ(1, pipefd[0], buf, sizeof(buf)); + + for (i = 0; i < sizeof(buf); i++) { + if (buf[i]) + fail++; + } + + if (fail) + tst_res(TFAIL, "%i non-zero bytes spliced from /dev/zero", fail); + else + tst_res(TPASS, "All bytes spliced from /dev/zero are zeroed"); + +ret: + SAFE_CLOSE(pipefd[0]); + SAFE_CLOSE(pipefd[1]); +} + +static void verify_splice(unsigned int n) +{ + unsigned int bytes = 1009 * n; + + tst_res(TINFO, "Splicing %u bytes from /dev/zero", bytes); + test_splice(bytes, fd_zero); + tst_res(TINFO, "Splicing %u bytes from /dev/full", bytes); + test_splice(bytes, fd_full); +} + +static void setup(void) +{ + fd_zero = SAFE_OPEN("/dev/zero", O_RDONLY); + fd_full = SAFE_OPEN("/dev/full", O_RDONLY); +} + +static void cleanup(void) +{ + if (fd_zero > 0) + SAFE_CLOSE(fd_zero); + + if (fd_full > 0) + SAFE_CLOSE(fd_full); +} + +static struct tst_test test = { + .test = verify_splice, + .tcnt = 9, + .setup = setup, + .cleanup = cleanup, + .min_kver = "6.7", +}; diff --git a/testcases/kernel/syscalls/splice/splice09.c b/testcases/kernel/syscalls/splice/splice09.c new file mode 100644 index 00000000..8863a013 --- /dev/null +++ b/testcases/kernel/syscalls/splice/splice09.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Cyril Hrubis + */ + +/*\ + * Test for splicing to /dev/zero and /dev/null these two devices discard all + * data written to them. + * + * The support for splicing to /dev/zero was added in: + * 1b057bd800c3 ("drivers/char/mem: implement splice() for /dev/zero, /dev/full") + */ + +#define _GNU_SOURCE +#include "tst_test.h" + +static const char *const test_devices[] = { + "/dev/null", + "/dev/zero", +}; + +static void verify_splice(unsigned int n) +{ + char buf[1024]; + char dev_fd; + int pipefd[2]; + + memset(buf, 0xff, sizeof(buf)); + + tst_res(TINFO, "Testing %s", test_devices[n]); + + dev_fd = SAFE_OPEN(test_devices[n], O_WRONLY); + + SAFE_PIPE(pipefd); + SAFE_WRITE(1, pipefd[1], buf, sizeof(buf)); + + TST_EXP_POSITIVE(splice(pipefd[0], NULL, dev_fd, NULL, sizeof(buf), 0)); + + if (TST_PASS && TST_RET != sizeof(buf)) + tst_res(TFAIL, "Wrote only part of the pipe buffer"); + + SAFE_CLOSE(pipefd[0]); + SAFE_CLOSE(pipefd[1]); + SAFE_CLOSE(dev_fd); +} + +static struct tst_test test = { + .test = verify_splice, + .tcnt = ARRAY_SIZE(test_devices), + .min_kver = "6.7", +}; diff --git a/testcases/kernel/syscalls/stat/.gitignore b/testcases/kernel/syscalls/stat/.gitignore index fa0a4ce9..0a62dc6e 100755 --- a/testcases/kernel/syscalls/stat/.gitignore +++ b/testcases/kernel/syscalls/stat/.gitignore @@ -4,3 +4,5 @@ /stat02_64 /stat03 /stat03_64 +/stat04 +/stat04_64 diff --git a/testcases/kernel/syscalls/stat/stat01.c b/testcases/kernel/syscalls/stat/stat01.c index 365cdac8..86aaa253 100755 --- a/testcases/kernel/syscalls/stat/stat01.c +++ b/testcases/kernel/syscalls/stat/stat01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that, stat(2) succeeds to get the status of a file and fills the * stat structure elements regardless of whether process has or doesn't * have read access to the file. diff --git a/testcases/kernel/syscalls/stat/stat03.c b/testcases/kernel/syscalls/stat/stat03.c index 369e6d79..6e08f507 100755 --- a/testcases/kernel/syscalls/stat/stat03.c +++ b/testcases/kernel/syscalls/stat/stat03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * check stat() with various error conditions that should produce * EACCES, EFAULT, ENAMETOOLONG, ENOENT, ENOTDIR, ELOOP */ diff --git a/testcases/kernel/syscalls/stat/stat04.c b/testcases/kernel/syscalls/stat/stat04.c new file mode 100644 index 00000000..09a9a682 --- /dev/null +++ b/testcases/kernel/syscalls/stat/stat04.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Author: David Fenner, Jon Hendrickson + * Copyright (C) 2024 Andrea Cervesato + */ + +/*\ + * This test checks that stat() executed on file provide the same information + * of symlink linking to it. + */ + +#include +#include "tst_test.h" +#include "tst_safe_stdio.h" + +#define FILENAME "file.txt" +#define MNTPOINT "mntpoint" +#define SYMBNAME MNTPOINT"/file_symlink" + +static char *symb_path; +static char *file_path; +static struct stat *file_stat; +static struct stat *symb_stat; + +static void run(void) +{ + SAFE_STAT(file_path, file_stat); + SAFE_STAT(symb_path, symb_stat); + + TST_EXP_EQ_LI(file_stat->st_dev, symb_stat->st_dev); + TST_EXP_EQ_LI(file_stat->st_mode, symb_stat->st_mode); + TST_EXP_EQ_LI(file_stat->st_nlink, symb_stat->st_nlink); + TST_EXP_EQ_LI(file_stat->st_uid, symb_stat->st_uid); + TST_EXP_EQ_LI(file_stat->st_gid, symb_stat->st_gid); + TST_EXP_EQ_LI(file_stat->st_size, symb_stat->st_size); + TST_EXP_EQ_LI(file_stat->st_atime, symb_stat->st_atime); + TST_EXP_EQ_LI(file_stat->st_mtime, symb_stat->st_mtime); + TST_EXP_EQ_LI(file_stat->st_ctime, symb_stat->st_ctime); +} + +static void setup(void) +{ + char opt_bsize[32]; + const char *const fs_opts[] = {opt_bsize, NULL}; + struct stat sb; + int pagesize; + int fd; + + file_path = tst_tmpdir_genpath(FILENAME); + symb_path = tst_tmpdir_genpath(SYMBNAME); + + /* change st_blksize / st_dev */ + SAFE_STAT(".", &sb); + pagesize = sb.st_blksize == 4096 ? 1024 : 4096; + + snprintf(opt_bsize, sizeof(opt_bsize), "-b %i", pagesize); + SAFE_MKFS(tst_device->dev, tst_device->fs_type, fs_opts, NULL); + SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, 0); + + SAFE_TOUCH(FILENAME, 0777, NULL); + + /* change st_nlink */ + SAFE_LINK(FILENAME, "linked_file"); + + /* change st_uid and st_gid */ + SAFE_CHOWN(FILENAME, 1000, 1000); + + /* change st_size */ + fd = SAFE_OPEN(FILENAME, O_WRONLY, 0777); + tst_fill_fd(fd, 'a', TST_KB, 500); + SAFE_CLOSE(fd); + + /* change st_atime / st_mtime / st_ctime */ + usleep(1001000); + + SAFE_SYMLINK(file_path, symb_path); +} + +static void cleanup(void) +{ + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); +} + +static struct tst_test test = { + .setup = setup, + .cleanup = cleanup, + .test_all = run, + .needs_root = 1, + .needs_device = 1, + .mntpoint = MNTPOINT, + .bufs = (struct tst_buffers []) { + {&file_stat, .size = sizeof(struct stat)}, + {&symb_stat, .size = sizeof(struct stat)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statfs/statfs01.c b/testcases/kernel/syscalls/statfs/statfs01.c index a2043f03..e88d5a30 100755 --- a/testcases/kernel/syscalls/statfs/statfs01.c +++ b/testcases/kernel/syscalls/statfs/statfs01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * AUTHOR : William Roske, CO-PILOT : Dave Fenner @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that statfs() syscall executes successfully on all * available filesystems. */ diff --git a/testcases/kernel/syscalls/statfs/statfs02.c b/testcases/kernel/syscalls/statfs/statfs02.c index ee9dba0c..c0444d75 100755 --- a/testcases/kernel/syscalls/statfs/statfs02.c +++ b/testcases/kernel/syscalls/statfs/statfs02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests for failures: * * - ENOTDIR A component of the pathname, which is not a directory. diff --git a/testcases/kernel/syscalls/statfs/statfs03.c b/testcases/kernel/syscalls/statfs/statfs03.c index 2afdbbf0..62b8044c 100755 --- a/testcases/kernel/syscalls/statfs/statfs03.c +++ b/testcases/kernel/syscalls/statfs/statfs03.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Verify that statfs(2) fails with errno EACCES when search permission * is denied for a component of the path prefix of path. */ diff --git a/testcases/kernel/syscalls/statmount/.gitignore b/testcases/kernel/syscalls/statmount/.gitignore new file mode 100644 index 00000000..dc1b1896 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/.gitignore @@ -0,0 +1,9 @@ +statmount01 +statmount02 +statmount03 +statmount04 +statmount05 +statmount06 +statmount07 +statmount08 +statmount09 diff --git a/testcases/kernel/syscalls/statmount/Makefile b/testcases/kernel/syscalls/statmount/Makefile new file mode 100644 index 00000000..8cf1b902 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC Andrea Cervesato + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/statmount/statmount.h b/testcases/kernel/syscalls/statmount/statmount.h new file mode 100644 index 00000000..d21d7f8d --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +#ifndef STATMOUNT_H +#define STATMOUNT_H + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/mount.h" +#include "lapi/syscalls.h" +#include "tst_safe_stdio.h" + +static inline int statmount(uint64_t mnt_id, uint64_t mask, struct statmount *buf, + size_t bufsize, unsigned int flags) +{ + struct mnt_id_req req = { + .size = MNT_ID_REQ_SIZE_VER0, + .mnt_id = mnt_id, + .param = mask, + }; + + return tst_syscall(__NR_statmount, &req, buf, bufsize, flags); +} + +static inline int read_peer_group(const char *path) +{ + FILE *file; + char line[PATH_MAX]; + char mroot[PATH_MAX]; + int group = -1; + + file = SAFE_FOPEN("/proc/self/mountinfo", "r"); + + while (fgets(line, sizeof(line), file)) { + if (sscanf(line, "%*d %*d %*d:%*d %s %*s %*s shared:%d", mroot, &group) != 2) + continue; + + if (strcmp(mroot, path) == 0) + break; + } + + if (group == -1) + tst_brk(TBROK, "Can't reed peer group ID for %s", path); + + return group; +} + +#endif diff --git a/testcases/kernel/syscalls/statmount/statmount01.c b/testcases/kernel/syscalls/statmount/statmount01.c new file mode 100644 index 00000000..fcf73e6a --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount01.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that statmount() is working with no mask flags. + * + * [Algorithm] + * + * - create a mount point + * - run statmount() on the mount point without giving any mask + * - read results and check that mask and size are correct + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" +#include "lapi/sched.h" + +#define MNTPOINT "mntpoint" + +static uint64_t mntpoint_id; +static struct statmount *st_mount; + +static void run(void) +{ + memset(st_mount, 0, sizeof(struct statmount)); + + TST_EXP_PASS(statmount(mntpoint_id, 0, st_mount, sizeof(struct statmount), 0)); + + if (!TST_PASS) + return; + + TST_EXP_EQ_LI(st_mount->size, sizeof(struct statmount)); + TST_EXP_EQ_LI(st_mount->mask, 0); + TST_EXP_EQ_LI(st_mount->sb_dev_major, 0); + TST_EXP_EQ_LI(st_mount->sb_dev_minor, 0); + TST_EXP_EQ_LI(st_mount->sb_magic, 0); + TST_EXP_EQ_LI(st_mount->sb_flags, 0); + TST_EXP_EQ_LI(st_mount->fs_type, 0); + TST_EXP_EQ_LI(st_mount->mnt_id, 0); + TST_EXP_EQ_LI(st_mount->mnt_parent_id, 0); + TST_EXP_EQ_LI(st_mount->mnt_id_old, 0); + TST_EXP_EQ_LI(st_mount->mnt_parent_id_old, 0); + TST_EXP_EQ_LI(st_mount->mnt_attr, 0); + TST_EXP_EQ_LI(st_mount->mnt_propagation, 0); + TST_EXP_EQ_LI(st_mount->mnt_peer_group, 0); + TST_EXP_EQ_LI(st_mount->mnt_master, 0); + TST_EXP_EQ_LI(st_mount->propagate_from, 0); + TST_EXP_EQ_LI(st_mount->mnt_root, 0); + TST_EXP_EQ_LI(st_mount->mnt_point, 0); +} + +static void setup(void) +{ + struct ltp_statx sx; + + SAFE_STATX(AT_FDCWD, MNTPOINT, 0, STATX_MNT_ID_UNIQUE, &sx); + + mntpoint_id = sx.data.stx_mnt_id; +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .min_kver = "6.8", + .mount_device = 1, + .mntpoint = MNTPOINT, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = sizeof(struct statmount)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount02.c b/testcases/kernel/syscalls/statmount/statmount02.c new file mode 100644 index 00000000..f322fe93 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount02.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that statmount() is correctly reading basic filesystem + * info using STATMOUNT_SB_BASIC. + * The btrfs validation is currently skipped due to the lack of support for VFS. + * + * [Algorithm] + * + * - create a mount point and read its mount info + * - run statmount() on the mount point using STATMOUNT_SB_BASIC + * - read results and check if mount info are correct + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" +#include "lapi/sched.h" + +#define MNTPOINT "mntpoint" + +static struct statmount *st_mount; +static struct ltp_statx *sx_mount; +static struct statfs *sf_mount; + +static void run(void) +{ + memset(st_mount, 0, sizeof(struct statmount)); + + TST_EXP_PASS(statmount(sx_mount->data.stx_mnt_id, STATMOUNT_SB_BASIC, + st_mount, sizeof(struct statmount), 0)); + + if (!TST_PASS) + return; + + TST_EXP_EQ_LI(st_mount->mask, STATMOUNT_SB_BASIC); + TST_EXP_EQ_LI(st_mount->size, sizeof(struct statmount)); + TST_EXP_EQ_LI(st_mount->sb_dev_major, sx_mount->data.stx_dev_major); + TST_EXP_EQ_LI(st_mount->sb_dev_minor, sx_mount->data.stx_dev_minor); + TST_EXP_EQ_LI(st_mount->sb_magic, sf_mount->f_type); + TST_EXP_EQ_LI(st_mount->sb_flags, MS_RDONLY); +} + +static void setup(void) +{ + SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, MS_RDONLY, NULL); + + SAFE_STATX(AT_FDCWD, MNTPOINT, 0, STATX_MNT_ID_UNIQUE, sx_mount); + SAFE_STATFS(MNTPOINT, sf_mount); +} + +static void cleanup(void) +{ + if (tst_is_mounted(MNTPOINT)) + SAFE_UMOUNT(MNTPOINT); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .min_kver = "6.8", + .mntpoint = MNTPOINT, + .format_device = 1, + .all_filesystems = 1, + .skip_filesystems = (const char *const []) { + "btrfs", + NULL + }, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = sizeof(struct statmount)}, + {&sx_mount, .size = sizeof(struct ltp_statx)}, + {&sf_mount, .size = sizeof(struct statfs)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount03.c b/testcases/kernel/syscalls/statmount/statmount03.c new file mode 100644 index 00000000..36250c1c --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount03.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that statmount() is correctly reading mount information + * (mount id, parent mount id, mount attributes etc.) using STATMOUNT_MNT_BASIC. + * + * [Algorithm] + * + * - create a mount point + * - create a new parent folder inside the mount point and obtain its mount info + * - create the new "/" mount folder and obtain its mount info + * - run statmount() on the mount point using STATMOUNT_MNT_BASIC + * - read results and check if mount info are correct + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" +#include "lapi/sched.h" + +#define DIR_A "LTP_DIR_A" +#define DIR_B "LTP_DIR_B" + +static uint64_t mnt_id; +static uint64_t mnt_id_old; +static uint64_t parent_id; +static uint64_t parent_id_old; +static uint64_t mnt_peer_group; +static uint64_t mnt_master; +static struct statmount *st_mount; + +static void read_mnt_id( + const char *path, + uint64_t *mnt_id, + uint64_t *mnt_id_unique) +{ + struct ltp_statx sx; + + if (mnt_id) { + sx.data.stx_mask = STATX_MNT_ID; + + SAFE_STATX(AT_FDCWD, path, 0, STATX_MNT_ID, &sx); + *mnt_id = sx.data.stx_mnt_id; + } + + if (mnt_id_unique) { + sx.data.stx_mask = STATX_MNT_ID_UNIQUE; + + SAFE_STATX(AT_FDCWD, path, 0, STATX_MNT_ID_UNIQUE, &sx); + *mnt_id_unique = sx.data.stx_mnt_id; + } +} + +static struct tcase { + uint64_t prop_type; + char *msg; +} tcases[] = { + { MS_PRIVATE, TST_TO_STR_(MS_PRIVATE) }, + { MS_SHARED, TST_TO_STR_(MS_SHARED) }, + { MS_SLAVE, TST_TO_STR_(MS_SLAVE) }, + { MS_UNBINDABLE, TST_TO_STR_(MS_UNBINDABLE) }, +}; + +static void run(unsigned int i) +{ + struct tcase *tc = &tcases[i]; + + tst_res(TINFO, "Testing statmount() on %s", tc->msg); + + SAFE_MOUNT(DIR_B, DIR_A, "none", MS_BIND, NULL); + SAFE_MOUNT("none", DIR_A, "none", tc->prop_type, NULL); + read_mnt_id(DIR_A, &mnt_id_old, &mnt_id); + + memset(st_mount, 0, sizeof(struct statmount)); + + TST_EXP_PASS(statmount(mnt_id, STATMOUNT_MNT_BASIC, st_mount, + sizeof(struct statmount), 0)); + + SAFE_UMOUNT(DIR_A); + + if (!TST_PASS) + return; + + mnt_peer_group = tc->prop_type == MS_SHARED ? read_peer_group(DIR_A) : 0; + mnt_master = tc->prop_type == MS_SLAVE ? read_peer_group(DIR_A) : 0; + + TST_EXP_EQ_LI(st_mount->mask, STATMOUNT_MNT_BASIC); + TST_EXP_EQ_LI(st_mount->size, sizeof(struct statmount)); + TST_EXP_EQ_LI(st_mount->mnt_id, mnt_id); + TST_EXP_EQ_LI(st_mount->mnt_id_old, mnt_id_old); + TST_EXP_EQ_LI(st_mount->mnt_parent_id, parent_id); + TST_EXP_EQ_LI(st_mount->mnt_parent_id_old, parent_id_old); + TST_EXP_EQ_LU(st_mount->mnt_propagation & tc->prop_type, tc->prop_type); + TST_EXP_EQ_LI(st_mount->mnt_master, mnt_master); + TST_EXP_EQ_LI(st_mount->mnt_peer_group, mnt_peer_group); +} + +static void setup(void) +{ + SAFE_MKDIR(DIR_A, 0700); + SAFE_MKDIR(DIR_B, 0700); + + SAFE_UNSHARE(CLONE_NEWNS); + SAFE_MOUNT("none", "/", "none", MS_REC | MS_PRIVATE, NULL); + + SAFE_MOUNT(DIR_B, DIR_B, "none", MS_BIND, NULL); + SAFE_MOUNT("none", DIR_B, "none", MS_SHARED, NULL); + + read_mnt_id(DIR_A, &parent_id_old, &parent_id); +} + +static void cleanup(void) +{ + if (tst_is_mounted(DIR_B)) + SAFE_UMOUNT(DIR_B); + + if (tst_is_mounted(DIR_A)) + SAFE_UMOUNT(DIR_A); +} + +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .cleanup = cleanup, + .min_kver = "6.8", + .needs_tmpdir = 1, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = sizeof(struct statmount)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount04.c b/testcases/kernel/syscalls/statmount/statmount04.c new file mode 100644 index 00000000..332f6dd4 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount04.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that statmount() is correctly reading propagation from + * what mount in current namespace using STATMOUNT_PROPAGATE_FROM. + * + * [Algorithm] + * + * - create a mount point + * - propagate a mounted folder inside the mount point + * - run statmount() on the mount point using STATMOUNT_PROPAGATE_FROM + * - read results and check propagated_from parameter contains the propagated + * folder ID + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" +#include "lapi/sched.h" + +#define MNTPOINT "mntpoint" +#define DIR_A MNTPOINT "/LTP_DIR_A" +#define DIR_C_SUBFOLDER "/LTP_DIR_A/propagated" +#define DIR_C (MNTPOINT DIR_C_SUBFOLDER) +#define DIR_B MNTPOINT "/LTP_DIR_B" +#define DIR_D MNTPOINT "/LTP_DIR_B/propagated" + +static uint64_t peer_group_id; +static uint64_t dird_id; +static struct statmount *st_mount; + +static void run(void) +{ + memset(st_mount, 0, sizeof(struct statmount)); + + TST_EXP_PASS(statmount(dird_id, STATMOUNT_PROPAGATE_FROM, st_mount, + sizeof(struct statmount), 0)); + + if (!TST_PASS) + return; + + TST_EXP_EQ_LI(st_mount->mask, STATMOUNT_PROPAGATE_FROM); + TST_EXP_EQ_LI(st_mount->size, sizeof(struct statmount)); + TST_EXP_EQ_LI(st_mount->propagate_from, peer_group_id); +} + +static void setup(void) +{ + struct ltp_statx sx; + + /* create DIR_A / DIR_C structure with DIR_C mounted */ + SAFE_MKDIR(DIR_A, 0700); + SAFE_MOUNT(DIR_A, DIR_A, "none", MS_BIND, NULL); + SAFE_MOUNT("none", DIR_A, "none", MS_SHARED, NULL); + + SAFE_MKDIR(DIR_C, 0700); + SAFE_MOUNT(DIR_C, DIR_C, "none", MS_BIND, NULL); + SAFE_MOUNT("none", DIR_C, "none", MS_SHARED, NULL); + + /* DIR_A mounts into DIR_B. DIR_D is propagated */ + SAFE_MKDIR(DIR_B, 0700); + SAFE_MOUNT(DIR_A, DIR_B, "none", MS_BIND, NULL); + SAFE_MOUNT("none", DIR_B, "none", MS_SLAVE, NULL); + + SAFE_STATX(AT_FDCWD, DIR_D, 0, STATX_MNT_ID_UNIQUE, &sx); + dird_id = sx.data.stx_mnt_id; + + peer_group_id = read_peer_group(DIR_A); +} + +static void cleanup(void) +{ + if (tst_is_mounted(DIR_C)) + SAFE_UMOUNT(DIR_C); + + if (tst_is_mounted(DIR_B)) + SAFE_UMOUNT(DIR_B); + + if (tst_is_mounted(DIR_A)) + SAFE_UMOUNT(DIR_A); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .min_kver = "6.8", + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = sizeof(struct statmount)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount05.c b/testcases/kernel/syscalls/statmount/statmount05.c new file mode 100644 index 00000000..56a2cb32 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount05.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies STATMOUNT_MNT_ROOT and STATMOUNT_MNT_POINT functionalities + * of statmount(). In particular, STATMOUNT_MNT_ROOT will give the mount root + * (i.e. mount --bind /mnt /bla -> /mnt) and STATMOUNT_MNT_POINT will + * give the mount point (i.e. mount --bind /mnt /bla -> /bla). + * + * [Algorithm] + * + * - create a mount point + * - mount a folder inside the mount point + * - run statmount() on the mounted folder using STATMOUNT_MNT_ROOT + * - read results and check if contain the mount root path + * - run statmount() on the mounted folder using STATMOUNT_MNT_POINT + * - read results and check if contain the mount point path + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" +#include "lapi/sched.h" +#include "tst_tmpdir.h" + +#define MNTPOINT "mntpoint" +#define DIRA MNTPOINT "/LTP_DIR_A" +#define DIRB MNTPOINT "/LTP_DIR_B" +#define SM_SIZE (1 << 10) + +static uint64_t root_id; +static struct statmount *st_mount; +static char *mnt_root; +static char *mnt_point; + +static void test_mount_root(void) +{ + tst_res(TINFO, "Testing STATMOUNT_MNT_ROOT"); + + char *last_root; + + memset(st_mount, 0, SM_SIZE); + + TST_EXP_PASS(statmount(root_id, STATMOUNT_MNT_ROOT, st_mount, + SM_SIZE, 0)); + + if (!TST_PASS) + return; + + last_root = strrchr(mnt_root, '/'); + + TST_EXP_EQ_LI(st_mount->mask, STATMOUNT_MNT_ROOT); + TST_EXP_EQ_STR(st_mount->str + st_mount->mnt_root, last_root); +} + +static void test_mount_point(void) +{ + tst_res(TINFO, "Testing STATMOUNT_MNT_POINT"); + + memset(st_mount, 0, SM_SIZE); + + TST_EXP_POSITIVE(statmount(root_id, STATMOUNT_MNT_POINT, st_mount, + SM_SIZE, 0)); + + if (!TST_PASS) + return; + + TST_EXP_EQ_LI(st_mount->mask, STATMOUNT_MNT_POINT); + TST_EXP_EQ_STR(st_mount->str + st_mount->mnt_point, mnt_point); +} + +static void run(void) +{ + test_mount_root(); + test_mount_point(); +} + +static void setup(void) +{ + struct ltp_statx sx; + + mnt_root = tst_tmpdir_genpath(DIRA); + mnt_point = tst_tmpdir_genpath(DIRB); + + SAFE_MKDIR(mnt_root, 0700); + SAFE_MKDIR(mnt_point, 0700); + SAFE_MOUNT(mnt_root, mnt_point, "none", MS_BIND, NULL); + + SAFE_STATX(AT_FDCWD, mnt_point, 0, STATX_MNT_ID_UNIQUE, &sx); + root_id = sx.data.stx_mnt_id; +} + +static void cleanup(void) +{ + if (tst_is_mounted(DIRB)) + SAFE_UMOUNT(DIRB); + + if (tst_is_mounted(DIRA)) + SAFE_UMOUNT(DIRA); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .min_kver = "6.8", + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = SM_SIZE}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount06.c b/testcases/kernel/syscalls/statmount/statmount06.c new file mode 100644 index 00000000..89717a3f --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount06.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that statmount() is correctly reading name of the + * filesystem type using STATMOUNT_FS_TYPE. + * + * [Algorithm] + * + * - create a mount point + * - run statmount() on the mount point using STATMOUNT_FS_TYPE + * - read results and check if contain the name of the filesystem + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" +#include "lapi/sched.h" + +#define MNTPOINT "mntpoint" +#define SM_SIZE (1 << 10) + +static uint64_t root_id; +static struct statmount *st_mount; + +static void run(void) +{ + memset(st_mount, 0, SM_SIZE); + + TST_EXP_PASS(statmount(root_id, STATMOUNT_FS_TYPE, st_mount, + SM_SIZE, 0)); + + if (!TST_PASS) + return; + + const char *fs_type = tst_device->is_fuse ? "fuseblk" : tst_device->fs_type; + + TST_EXP_EQ_LI(st_mount->mask, STATMOUNT_FS_TYPE); + TST_EXP_EQ_STR(st_mount->str + st_mount->fs_type, fs_type); +} + +static void setup(void) +{ + struct ltp_statx sx; + + SAFE_STATX(AT_FDCWD, MNTPOINT, 0, STATX_MNT_ID_UNIQUE, &sx); + root_id = sx.data.stx_mnt_id; +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .min_kver = "6.8", + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = SM_SIZE}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount07.c b/testcases/kernel/syscalls/statmount/statmount07.c new file mode 100644 index 00000000..2053dcc6 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount07.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that statmount() is raising the correct errors according + * with invalid input values. + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" + +#define MNTPOINT "mntpoint" + +static struct statmount *st_mount; +static struct statmount *st_mount_null; +static struct statmount *st_mount_small; +static struct statmount *st_mount_bad; +static uint64_t mnt_id; +static uint64_t mnt_id_dont_exist = -1; +static size_t buff_size; + +struct tcase { + int exp_errno; + char *msg; + uint64_t *mnt_id; + uint64_t mask; + unsigned int flags; + struct statmount **buff; +} tcases[] = { + { + .exp_errno = ENOENT, + .msg = "mount id doesn't exist'", + .mnt_id = &mnt_id_dont_exist, + .buff = &st_mount + }, + { + .exp_errno = EOVERFLOW, + .msg = "invalid mask", + .mnt_id = &mnt_id, + .mask = -1, + .buff = &st_mount + }, + { + .exp_errno = EOVERFLOW, + .msg = "small buffer for fs type", + .mnt_id = &mnt_id, + .mask = STATMOUNT_FS_TYPE, + .buff = &st_mount_small, + }, + { + .exp_errno = EOVERFLOW, + .msg = "small buffer for mnt root", + .mnt_id = &mnt_id, + .mask = STATMOUNT_MNT_ROOT, + .buff = &st_mount_small, + }, + { + .exp_errno = EOVERFLOW, + .msg = "small buffer for mnt point", + .mnt_id = &mnt_id, + .mask = STATMOUNT_MNT_POINT, + .buff = &st_mount_small, + }, + { + .exp_errno = EINVAL, + .msg = "flags must be zero", + .mnt_id = &mnt_id, + .flags = 1, + .buff = &st_mount, + }, + { + .exp_errno = EFAULT, + .msg = "buffer crosses to PROT_NONE", + .mnt_id = &mnt_id, + .buff = &st_mount_bad, + }, + { + .exp_errno = EFAULT, + .msg = "invalid buffer pointer", + .mnt_id = &mnt_id, + .buff = &st_mount_null, + }, +}; + +static void run(unsigned int n) +{ + struct tcase *tc = &tcases[n]; + + memset(st_mount, 0, sizeof(struct statmount)); + + TST_EXP_FAIL(statmount(*tc->mnt_id, tc->mask, + *tc->buff, buff_size, tc->flags), + tc->exp_errno, "%s", tc->msg); +} + +static void setup(void) +{ + struct ltp_statx sx; + + SAFE_STATX(AT_FDCWD, MNTPOINT, 0, STATX_MNT_ID_UNIQUE, &sx); + + mnt_id = sx.data.stx_mnt_id; + buff_size = sizeof(struct statmount); +} +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .min_kver = "6.8", + .mount_device = 1, + .mntpoint = MNTPOINT, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = sizeof(struct statmount)}, + {&st_mount_small, .size = sizeof(struct statmount)}, + {&st_mount_bad, .size = 1}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount08.c b/testcases/kernel/syscalls/statmount/statmount08.c new file mode 100644 index 00000000..b77116f0 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount08.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that statmount() raises EPERM when mount point is not accessible. + */ + +#define _GNU_SOURCE + +#include +#include "statmount.h" +#include "lapi/stat.h" + +static struct statmount *st_mount; +static uint64_t root_id; +static gid_t nobody_gid; +static uid_t nobody_uid; + +static void run(void) +{ + if (SAFE_FORK()) + return; + + SAFE_SETEGID(nobody_gid); + SAFE_SETEUID(nobody_uid); + + memset(st_mount, 0, sizeof(struct statmount)); + + TST_EXP_FAIL(statmount(root_id, STATMOUNT_SB_BASIC, st_mount, + sizeof(struct statmount), 0), EPERM); + + exit(0); +} + +static void setup(void) +{ + struct ltp_statx sx; + struct passwd *pw; + + pw = SAFE_GETPWNAM("nobody"); + nobody_gid = pw->pw_gid; + nobody_uid = pw->pw_uid; + + SAFE_STATX(AT_FDCWD, "/", 0, STATX_MNT_ID_UNIQUE, &sx); + root_id = sx.data.stx_mnt_id; + + SAFE_CHROOT(tst_tmpdir_path()); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .needs_root = 1, + .needs_tmpdir = 1, + .forks_child = 1, + .min_kver = "6.8", + .bufs = (struct tst_buffers []) { + {&st_mount, .size = sizeof(struct statmount)}, + {} + } +}; diff --git a/testcases/kernel/syscalls/statmount/statmount09.c b/testcases/kernel/syscalls/statmount/statmount09.c new file mode 100644 index 00000000..eaa882b8 --- /dev/null +++ b/testcases/kernel/syscalls/statmount/statmount09.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test verifies that statmount() is correctly reading the mount ID for + * the current namespace. + * + * [Algorithm] + * + * - create a mount point + * - run statmount() on the mount point using STATMOUNT_MNT_NS_ID + * - read results and check if contain the correct mount ID for the current + * namespace + */ + +#define _GNU_SOURCE + +#include "statmount.h" +#include "lapi/stat.h" +#include "lapi/ioctl_ns.h" + +#define MNTPOINT "mntpoint" +#define SM_SIZE (1 << 10) + +static uint64_t root_id; +static u_int64_t mnt_ns_id; +static struct statmount *st_mount; + +static void run(void) +{ + memset(st_mount, 0, SM_SIZE); + + TST_EXP_PASS(statmount(root_id, STATMOUNT_MNT_NS_ID, st_mount, + SM_SIZE, 0)); + + if (!TST_PASS) + return; + + TST_EXP_EQ_LI(st_mount->mask, STATMOUNT_MNT_NS_ID); +#if !defined(HAVE_STRUCT_STATMOUNT) || defined(HAVE_STRUCT_STATMOUNT_MNT_NS_ID) + TST_EXP_EQ_LI(st_mount->mnt_ns_id, mnt_ns_id); +#else + tst_res(TCONF, "statmount.mnt_ns_id not available in current headers"); +#endif +} + +static void setup(void) +{ + struct ltp_statx sx; + int fd; + + SAFE_STATX(AT_FDCWD, MNTPOINT, 0, STATX_MNT_ID_UNIQUE, &sx); + root_id = sx.data.stx_mnt_id; + + fd = SAFE_OPEN("/proc/self/ns/mnt", O_RDONLY); + SAFE_IOCTL(fd, NS_GET_MNTNS_ID, &mnt_ns_id); + SAFE_CLOSE(fd); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .min_kver = "6.11", + .mount_device = 1, + .mntpoint = MNTPOINT, + .all_filesystems = 1, + .skip_filesystems = (const char *const []) { + "fuse", + NULL + }, + .bufs = (struct tst_buffers []) { + {&st_mount, .size = SM_SIZE}, + {} + } +}; + diff --git a/testcases/kernel/syscalls/statvfs/statvfs01.c b/testcases/kernel/syscalls/statvfs/statvfs01.c index c10c67b1..4e901a0a 100755 --- a/testcases/kernel/syscalls/statvfs/statvfs01.c +++ b/testcases/kernel/syscalls/statvfs/statvfs01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2005. All Rights Reserved. * AUTHOR: Prashant P Yendigeri @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that statvfs() executes successfully for all * available filesystems. Verify statvfs.f_namemax field * by trying to create files of valid and invalid length names. diff --git a/testcases/kernel/syscalls/statvfs/statvfs02.c b/testcases/kernel/syscalls/statvfs/statvfs02.c index 6f36530b..5ed30d31 100755 --- a/testcases/kernel/syscalls/statvfs/statvfs02.c +++ b/testcases/kernel/syscalls/statvfs/statvfs02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 Fujitsu Ltd. @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that statvfs() fails with: * * - EFAULT when path points to an invalid address. diff --git a/testcases/kernel/syscalls/statx/statx01.c b/testcases/kernel/syscalls/statx/statx01.c index f9c2748d..dc63659c 100755 --- a/testcases/kernel/syscalls/statx/statx01.c +++ b/testcases/kernel/syscalls/statx/statx01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This code tests the functionality of statx system call. * * The metadata for normal file are tested against expected values: diff --git a/testcases/kernel/syscalls/statx/statx02.c b/testcases/kernel/syscalls/statx/statx02.c index 5ed80894..c8cb8987 100755 --- a/testcases/kernel/syscalls/statx/statx02.c +++ b/testcases/kernel/syscalls/statx/statx02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This code tests the following flags with statx syscall: * * - AT_EMPTY_PATH diff --git a/testcases/kernel/syscalls/statx/statx03.c b/testcases/kernel/syscalls/statx/statx03.c index 2465d877..4877a3e4 100755 --- a/testcases/kernel/syscalls/statx/statx03.c +++ b/testcases/kernel/syscalls/statx/statx03.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test basic error handling of statx syscall: * * - EBADF - Bad file descriptor diff --git a/testcases/kernel/syscalls/statx/statx04.c b/testcases/kernel/syscalls/statx/statx04.c index 58296bd2..af30a200 100755 --- a/testcases/kernel/syscalls/statx/statx04.c +++ b/testcases/kernel/syscalls/statx/statx04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test whether the kernel properly advertises support for statx() attributes: * * - STATX_ATTR_COMPRESSED: The file is compressed by the filesystem. @@ -96,8 +94,9 @@ static void setup(void) for (i = 0, expected_mask = 0; i < ARRAY_SIZE(attr_list); i++) expected_mask |= attr_list[i].attr; - /* STATX_ATTR_COMPRESSED not supported on XFS, TMPFS */ - if (!strcmp(tst_device->fs_type, "xfs") || !strcmp(tst_device->fs_type, "tmpfs")) + /* STATX_ATTR_COMPRESSED not supported on Bcachefs, TMPFS, XFS */ + if (!strcmp(tst_device->fs_type, "bcachefs") || !strcmp(tst_device->fs_type, "tmpfs") || + !strcmp(tst_device->fs_type, "xfs")) expected_mask &= ~STATX_ATTR_COMPRESSED; /* Attribute support was added to Btrfs statx() in kernel v4.13 */ diff --git a/testcases/kernel/syscalls/statx/statx05.c b/testcases/kernel/syscalls/statx/statx05.c index 9781b3e7..2a460322 100755 --- a/testcases/kernel/syscalls/statx/statx05.c +++ b/testcases/kernel/syscalls/statx/statx05.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test statx syscall with STATX_ATTR_ENCRYPTED flag, setting a key is required * for the file to be encrypted by the filesystem. * @@ -121,7 +119,10 @@ static struct tst_test test = { .needs_root = 1, .needs_device = 1, .mntpoint = MNTPOINT, - .dev_fs_type = "ext4", + .filesystems = (struct tst_fs []) { + {.type = "ext4"}, + {} + }, .needs_cmds = (const char *[]) { "mkfs.ext4 >= 1.43.0", "e4crypt", diff --git a/testcases/kernel/syscalls/statx/statx06.c b/testcases/kernel/syscalls/statx/statx06.c index 1771dff4..9795740c 100755 --- a/testcases/kernel/syscalls/statx/statx06.c +++ b/testcases/kernel/syscalls/statx/statx06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test the following file timestamps of statx syscall: * * - btime - The time before and after the execution of the create system call is noted. @@ -155,7 +153,12 @@ static struct tst_test test = { .needs_root = 1, .mntpoint = MOUNT_POINT, .mount_device = 1, - .dev_fs_type = "ext4", - .dev_fs_opts = (const char *const []){"-I", "256", NULL}, - .mnt_flags = MS_STRICTATIME, + .filesystems = (struct tst_fs[]) { + { + .type = "ext4", + .mkfs_opts = (const char *const []){"-I", "256", NULL}, + .mnt_flags = MS_STRICTATIME, + }, + {} + }, }; diff --git a/testcases/kernel/syscalls/statx/statx07.c b/testcases/kernel/syscalls/statx/statx07.c index 4dbf83e1..bab64591 100755 --- a/testcases/kernel/syscalls/statx/statx07.c +++ b/testcases/kernel/syscalls/statx/statx07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This code tests the following flags: * * - AT_STATX_FORCE_SYNC @@ -54,7 +52,6 @@ #define SERV_FORCE_SYNC "server/force_sync_file" #define SERV_DONT_SYNC "server/dont_sync_file" -static char *cwd; static char cmd[BUFF_SIZE]; static int mounted; static int exported; @@ -116,14 +113,19 @@ static void setup(void) int ret; char server_path[BUFF_SIZE]; - cwd = tst_get_tmpdir(); + if (access("/var/lib/nfs/etab", F_OK) < 0) + tst_brk(TCONF, "nfs-server might not set up"); + + mode_t old_umask = umask(0); SAFE_MKDIR(SERV_PATH, DEFAULT_MODE); SAFE_MKDIR(CLI_PATH, DEFAULT_MODE); SAFE_CREAT(SERV_FORCE_SYNC, DEFAULT_MODE); SAFE_CREAT(SERV_DONT_SYNC, DEFAULT_MODE); - snprintf(server_path, sizeof(server_path), ":%s/%s", cwd, SERV_PATH); + umask(old_umask); + + snprintf(server_path, sizeof(server_path), ":%s/%s", tst_tmpdir_path(), SERV_PATH); snprintf(cmd, sizeof(cmd), "exportfs -i -o no_root_squash,rw,sync,no_subtree_check,fsid=%d *%.1024s", @@ -151,7 +153,7 @@ static void cleanup(void) if (!exported) return; snprintf(cmd, sizeof(cmd), - "exportfs -u *:%s/%s", cwd, SERV_PATH); + "exportfs -u *:%s/%s", tst_tmpdir_path(), SERV_PATH); if (tst_system(cmd) == -1) tst_res(TWARN | TST_ERR, "failed to clear exportfs"); @@ -164,7 +166,10 @@ static struct tst_test test = { .cleanup = cleanup, .min_kver = "4.16", .needs_tmpdir = 1, - .dev_fs_type = "nfs", + .filesystems = (struct tst_fs []) { + {.type = "nfs"}, + {} + }, .needs_root = 1, .needs_cmds = (const char *[]) { "exportfs", diff --git a/testcases/kernel/syscalls/statx/statx08.c b/testcases/kernel/syscalls/statx/statx08.c index 64b36986..caced5d3 100755 --- a/testcases/kernel/syscalls/statx/statx08.c +++ b/testcases/kernel/syscalls/statx/statx08.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This case tests whether the attributes field of statx received expected value * by using flags in the stx_attributes_mask field of statx. * File set with following flags by using SAFE_IOCTL: diff --git a/testcases/kernel/syscalls/statx/statx09.c b/testcases/kernel/syscalls/statx/statx09.c index 6e75ff3e..ee4be425 100644 --- a/testcases/kernel/syscalls/statx/statx09.c +++ b/testcases/kernel/syscalls/statx/statx09.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This code tests if STATX_ATTR_VERITY flag in the statx attributes is set correctly. * * The statx() system call sets STATX_ATTR_VERITY if the file has fs-verity @@ -156,7 +154,10 @@ static struct tst_test test = { .needs_root = 1, .needs_device = 1, .mntpoint = MNTPOINT, - .dev_fs_type = "ext4", + .filesystems = (struct tst_fs []) { + {.type = "ext4"}, + {} + }, .needs_kconfigs = (const char *[]) { "CONFIG_FS_VERITY", NULL diff --git a/testcases/kernel/syscalls/statx/statx10.c b/testcases/kernel/syscalls/statx/statx10.c index 42106285..079fb67a 100644 --- a/testcases/kernel/syscalls/statx/statx10.c +++ b/testcases/kernel/syscalls/statx/statx10.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for STATX_DIOALIGN mask on ext4 and xfs filesystem. * * - STATX_DIOALIGN Want stx_dio_mem_align and stx_dio_offset_align value diff --git a/testcases/kernel/syscalls/statx/statx11.c b/testcases/kernel/syscalls/statx/statx11.c index 65305085..5465c265 100644 --- a/testcases/kernel/syscalls/statx/statx11.c +++ b/testcases/kernel/syscalls/statx/statx11.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for STATX_DIOALIGN mask on block device. * * - STATX_DIOALIGN Want stx_dio_mem_align and stx_dio_offset_align value diff --git a/testcases/kernel/syscalls/statx/statx12.c b/testcases/kernel/syscalls/statx/statx12.c index 432f1cb7..8b5393aa 100644 --- a/testcases/kernel/syscalls/statx/statx12.c +++ b/testcases/kernel/syscalls/statx/statx12.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * It is a basic test for STATX_ATTR_MOUNT_ROOT flag. * * This flag indicates whether the path or fd refers to the root of a mount diff --git a/testcases/kernel/syscalls/swapoff/Makefile b/testcases/kernel/syscalls/swapoff/Makefile index 6954112a..c39c321e 100755 --- a/testcases/kernel/syscalls/swapoff/Makefile +++ b/testcases/kernel/syscalls/swapoff/Makefile @@ -3,7 +3,7 @@ top_srcdir ?= ../../../.. -LTPLIBS = ltpswap +LTPLIBS = swap include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/swapoff/swapoff01.c b/testcases/kernel/syscalls/swapoff/swapoff01.c index b27eecda..bf097ac1 100755 --- a/testcases/kernel/syscalls/swapoff/swapoff01.c +++ b/testcases/kernel/syscalls/swapoff/swapoff01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Check that swapoff() succeeds. */ @@ -17,15 +15,19 @@ #include "lapi/syscalls.h" #include "libswap.h" +#define MNTPOINT "mntpoint" +#define TEST_FILE MNTPOINT"/testswap" +#define SWAP_FILE MNTPOINT"/swapfile" + static void verify_swapoff(void) { - if (tst_syscall(__NR_swapon, "./swapfile01", 0) != 0) { + if (tst_syscall(__NR_swapon, SWAP_FILE, 0) != 0) { tst_res(TFAIL | TERRNO, "Failed to turn on the swap file" ", skipping test iteration"); return; } - TEST(tst_syscall(__NR_swapoff, "./swapfile01")); + TEST(tst_syscall(__NR_swapoff, SWAP_FILE)); if (TST_RET == -1) { tst_res(TFAIL | TTERRNO, "Failed to turn off swapfile," @@ -38,22 +40,17 @@ static void verify_swapoff(void) static void setup(void) { - is_swap_supported("./tstswap"); - - if (!tst_fs_has_free(".", 64, TST_MB)) - tst_brk(TBROK, - "Insufficient disk space to create swap file"); - - if (tst_fill_file("swapfile01", 0x00, 1024, 65536)) - tst_brk(TBROK, "Failed to create file for swap"); - - if (system("mkswap swapfile01 > tmpfile 2>&1") != 0) - tst_brk(TBROK, "Failed to make swapfile"); + is_swap_supported(TEST_FILE); + SAFE_MAKE_SWAPFILE_BLKS(SWAP_FILE, 65536); } static struct tst_test test = { + .mntpoint = MNTPOINT, + .mount_device = 1, + .dev_min_size = 350, + .all_filesystems = 1, .needs_root = 1, - .needs_tmpdir = 1, .test_all = verify_swapoff, + .timeout = 60, .setup = setup }; diff --git a/testcases/kernel/syscalls/swapoff/swapoff02.c b/testcases/kernel/syscalls/swapoff/swapoff02.c index cd940b1e..b6c42898 100755 --- a/testcases/kernel/syscalls/swapoff/swapoff02.c +++ b/testcases/kernel/syscalls/swapoff/swapoff02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test case checks whether swapoff(2) system call returns * 1. EINVAL when the path does not exist * 2. ENOENT when the path exists but is invalid @@ -18,6 +16,10 @@ #include "lapi/syscalls.h" #include "libswap.h" +#define MNTPOINT "mntpoint" +#define TEST_FILE MNTPOINT"/testswap" +#define SWAP_FILE MNTPOINT"/swapfile" + static int setup01(void); static void cleanup01(void); @@ -32,8 +34,8 @@ static struct tcase { void (*cleanup)(void); } tcases[] = { {"path does not exist", ENOENT, "ENOENT", "./doesnotexist", NULL, NULL}, - {"Invalid file", EINVAL, "EINVAL", "./swapfile01", NULL, NULL}, - {"Permission denied", EPERM, "EPERM", "./swapfile01", setup01, cleanup01} + {"Invalid file", EINVAL, "EINVAL", SWAP_FILE, NULL, NULL}, + {"Permission denied", EPERM, "EPERM", SWAP_FILE, setup01, cleanup01} }; static void verify_swapoff(unsigned int i) @@ -82,18 +84,15 @@ static void setup(void) nobody = SAFE_GETPWNAM("nobody"); nobody_uid = nobody->pw_uid; - is_swap_supported("./tstswap"); - - if (!tst_fs_has_free(".", 1, TST_KB)) - tst_brk(TBROK, "Insufficient disk space to create swap file"); - - if (tst_fill_file("./swapfile01", 0x00, 1024, 1)) - tst_brk(TBROK, "Failed to create swapfile"); + is_swap_supported(TEST_FILE); + SAFE_MAKE_SMALL_SWAPFILE(SWAP_FILE); } static struct tst_test test = { + .mntpoint = MNTPOINT, + .mount_device = 1, + .all_filesystems = 1, .needs_root = 1, - .needs_tmpdir = 1, .test = verify_swapoff, .tcnt = ARRAY_SIZE(tcases), .setup = setup diff --git a/testcases/kernel/syscalls/swapon/Makefile b/testcases/kernel/syscalls/swapon/Makefile index 53c79509..c39c321e 100755 --- a/testcases/kernel/syscalls/swapon/Makefile +++ b/testcases/kernel/syscalls/swapon/Makefile @@ -1,14 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) International Business Machines Corp., 2001 -NEEDSPECIAL := $(shell echo MAX_SWAPFILES | $(CC) -E -xc -include linux/swap.h 2>/dev/null - | tail -n 1 | grep 32; echo $?) -ifneq ($(strip $(NEEDSPECIAL)),) -export CFLAGS += -DOLDER_DISTRO_RELEASE -endif - top_srcdir ?= ../../../.. -LTPLIBS = ltpswap +LTPLIBS = swap include $(top_srcdir)/include/mk/testcases.mk diff --git a/testcases/kernel/syscalls/swapon/swapon01.c b/testcases/kernel/syscalls/swapon/swapon01.c index c334ae24..a214bc9d 100755 --- a/testcases/kernel/syscalls/swapon/swapon01.c +++ b/testcases/kernel/syscalls/swapon/swapon01.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. + * Copyright (c) Linux Test Project, 2003-2024 */ /*\ - * [Description] - * * Checks that swapon() succeds with swapfile. + * Testing on all filesystems which support swap file. */ #include @@ -16,31 +16,39 @@ #include "lapi/syscalls.h" #include "libswap.h" +#define MNTPOINT "mntpoint" +#define SWAP_FILE MNTPOINT"/swapfile01" +#define TESTMEM (1UL<<30) + static void verify_swapon(void) { - TEST(tst_syscall(__NR_swapon, "./swapfile01", 0)); + TST_EXP_PASS(tst_syscall(__NR_swapon, SWAP_FILE, 0)); - if (TST_RET == -1) { - tst_res(TFAIL | TTERRNO, "Failed to turn on swapfile"); - } else { - tst_res(TPASS, "Succeeded to turn on swapfile"); - /*we need to turn this swap file off for -i option */ - if (tst_syscall(__NR_swapoff, "./swapfile01") != 0) { - tst_brk(TBROK | TERRNO, "Failed to turn off swapfile," - " system reboot after execution of LTP " - "test suite is recommended."); - } + tst_pollute_memory(TESTMEM, 0x41); + tst_res(TINFO, "SwapCached: %ld Kb", SAFE_READ_MEMINFO("SwapCached:")); + + if (TST_PASS && tst_syscall(__NR_swapoff, SWAP_FILE) != 0) { + tst_brk(TBROK | TERRNO, + "Failed to turn off swapfile, system reboot recommended"); } } static void setup(void) { - is_swap_supported("./tstswap"); - make_swapfile("swapfile01", 0); + is_swap_supported(SWAP_FILE); + MAKE_SWAPFILE_SIZE(SWAP_FILE, 128); + + SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid()); + SAFE_CG_PRINTF(tst_cg, "memory.max", "%lu", TESTMEM); } static struct tst_test test = { - .needs_tmpdir = 1, + .mntpoint = MNTPOINT, + .mount_device = 1, + .needs_root = 1, + .all_filesystems = 1, + .needs_cgroup_ctrls = (const char *const []){ "memory", NULL }, .test_all = verify_swapon, + .timeout = 60, .setup = setup }; diff --git a/testcases/kernel/syscalls/swapon/swapon02.c b/testcases/kernel/syscalls/swapon/swapon02.c index d34c17a8..625d463f 100755 --- a/testcases/kernel/syscalls/swapon/swapon02.c +++ b/testcases/kernel/syscalls/swapon/swapon02.c @@ -1,28 +1,29 @@ // SPDX-License-Identifier: GPL-2.0-or-later - /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. + * Copyright (c) Linux Test Project, 2002-2023 */ /*\ - * [Description] + * This test case checks whether swapon(2) system call returns: * - * This test case checks whether swapon(2) system call returns - * 1. ENOENT when the path does not exist - * 2. EINVAL when the path exists but is invalid - * 3. EPERM when user is not a superuser - * 4. EBUSY when the specified path is already being used as a swap area + * - ENOENT when the path does not exist + * - EINVAL when the path exists but is invalid + * - EPERM when user is not a superuser + * - EBUSY when the specified path is already being used as a swap area */ -#include #include #include "tst_test.h" #include "lapi/syscalls.h" #include "libswap.h" -static void setup01(void); -static void cleanup01(void); +#define MNTPOINT "mntpoint" +#define TEST_FILE MNTPOINT"/testswap" +#define NOTSWAP_FILE MNTPOINT"/notswap" +#define SWAP_FILE MNTPOINT"/swapfile" +#define USED_FILE MNTPOINT"/alreadyused" static uid_t nobody_uid; static int do_swapoff; @@ -30,27 +31,14 @@ static int do_swapoff; static struct tcase { char *err_desc; int exp_errno; - char *exp_errval; char *path; - void (*setup)(void); - void (*cleanup)(void); } tcases[] = { - {"Path does not exist", ENOENT, "ENOENT", "./doesnotexist", NULL, NULL}, - {"Invalid path", EINVAL, "EINVAL", "./notswap", NULL, NULL}, - {"Permission denied", EPERM, "EPERM", "./swapfile01", setup01, cleanup01}, - {"File already used", EBUSY, "EBUSY", "./alreadyused", NULL, NULL}, + {"Path does not exist", ENOENT, "./doesnotexist"}, + {"Invalid path", EINVAL, NOTSWAP_FILE}, + {"Permission denied", EPERM, SWAP_FILE}, + {"File already used", EBUSY, USED_FILE}, }; -static void setup01(void) -{ - SAFE_SETEUID(nobody_uid); -} - -static void cleanup01(void) -{ - SAFE_SETEUID(0); -} - static void setup(void) { struct passwd *nobody; @@ -58,50 +46,47 @@ static void setup(void) nobody = SAFE_GETPWNAM("nobody"); nobody_uid = nobody->pw_uid; - is_swap_supported("./tstswap"); + is_swap_supported(TEST_FILE); - SAFE_TOUCH("notswap", 0777, NULL); - make_swapfile("swapfile01", 0); - make_swapfile("alreadyused", 0); + SAFE_TOUCH(NOTSWAP_FILE, 0777, NULL); + MAKE_SMALL_SWAPFILE(SWAP_FILE); + MAKE_SMALL_SWAPFILE(USED_FILE); - if (tst_syscall(__NR_swapon, "alreadyused", 0)) + if (tst_syscall(__NR_swapon, USED_FILE, 0)) tst_res(TWARN | TERRNO, "swapon(alreadyused) failed"); else do_swapoff = 1; } -void cleanup(void) +static void cleanup(void) { - if (do_swapoff && tst_syscall(__NR_swapoff, "alreadyused")) + if (do_swapoff && tst_syscall(__NR_swapoff, USED_FILE)) tst_res(TWARN | TERRNO, "swapoff(alreadyused) failed"); } static void verify_swapon(unsigned int i) { struct tcase *tc = tcases + i; - if (tc->setup) - tc->setup(); + if (tc->exp_errno == EPERM) + SAFE_SETEUID(nobody_uid); - TEST(tst_syscall(__NR_swapon, tc->path, 0)); + TST_EXP_FAIL(tst_syscall(__NR_swapon, tc->path, 0), tc->exp_errno, + "swapon(2) fail with %s", tc->err_desc); - if (tc->cleanup) - tc->cleanup(); + if (tc->exp_errno == EPERM) + SAFE_SETEUID(0); - if (TST_RET == -1 && TST_ERR == tc->exp_errno) { - tst_res(TPASS, "swapon(2) expected failure;" - " Got errno - %s : %s", - tc->exp_errval, tc->err_desc); - return; + if (TST_RET != -1) { + tst_res(TFAIL, "swapon(2) failed unexpectedly, expected: %s", + tst_strerrno(tc->exp_errno)); } - - tst_res(TFAIL, "swapon(2) failed to produce expected error:" - " %d, errno: %s and got %d.", tc->exp_errno, - tc->exp_errval, TST_ERR); } static struct tst_test test = { + .mntpoint = MNTPOINT, + .mount_device = 1, + .all_filesystems = 1, .needs_root = 1, - .needs_tmpdir = 1, .test = verify_swapon, .tcnt = ARRAY_SIZE(tcases), .setup = setup, diff --git a/testcases/kernel/syscalls/swapon/swapon03.c b/testcases/kernel/syscalls/swapon/swapon03.c index dc633ebc..0068560f 100755 --- a/testcases/kernel/syscalls/swapon/swapon03.c +++ b/testcases/kernel/syscalls/swapon/swapon03.c @@ -6,9 +6,8 @@ */ /*\ - * [Description] - * * This test case checks whether swapon(2) system call returns: + * * - EPERM when there are more than MAX_SWAPFILES already in use. */ @@ -16,203 +15,53 @@ #include #include #include - +#include #include "tst_test.h" #include "lapi/syscalls.h" -#include "swaponoff.h" #include "libswap.h" -static int setup_swap(void); -static int clean_swap(void); -static int check_and_swapoff(const char *filename); +#define MNTPOINT "mntpoint" +#define TEST_FILE MNTPOINT"/testswap" static int swapfiles; -int testfiles = 3; -static struct swap_testfile_t { - char *filename; -} swap_testfiles[] = { - {"firstswapfile"}, - {"secondswapfile"}, - {"thirdswapfile"} -}; - -int expected_errno = EPERM; - -static void verify_swapon(void) -{ - if (setup_swap() < 0) { - clean_swap(); - tst_brk(TBROK, "Setup failed, quitting the test"); - } - - TEST(tst_syscall(__NR_swapon, swap_testfiles[0].filename, 0)); - - if ((TST_RET == -1) && (TST_ERR == expected_errno)) { - tst_res(TPASS, "swapon(2) got expected failure (%d),", - expected_errno); - } else if (TST_RET < 0) { - tst_res(TFAIL | TTERRNO, - "swapon(2) failed to produce expected error " - "(%d). System reboot recommended.", - expected_errno); - } else { - /* Probably the system supports MAX_SWAPFILES > 30, - * let's try with MAX_SWAPFILES == 32 */ - - /* Call swapon sys call once again for 32 - * now we can't receive an error */ - TEST(tst_syscall(__NR_swapon, swap_testfiles[1].filename, 0)); - - /* Check return code (now we're expecting success) */ - if (TST_RET < 0) { - tst_res(TFAIL | TTERRNO, - "swapon(2) got an unexpected failure"); - } else { - /* Call swapon sys call once again for 33 - * now we have to receive an error */ - TEST(tst_syscall(__NR_swapon, swap_testfiles[2].filename, 0)); - - /* Check return code (should be an error) */ - if ((TST_RET == -1) && (TST_ERR == expected_errno)) { - tst_res(TPASS, - "swapon(2) got expected failure;" - " Got errno = %d, probably your" - " MAX_SWAPFILES is 32", - expected_errno); - } else { - tst_res(TFAIL, - "swapon(2) failed to produce" - " expected error: %d, got %s." - " System reboot after execution of LTP" - " test suite is recommended.", - expected_errno, strerror(TST_ERR)); - } - } - } - - if (clean_swap() < 0) - tst_brk(TBROK, "Cleanup failed, quitting the test"); -} - -/* - * Create 33 and activate 30 swapfiles. - */ static int setup_swap(void) { pid_t pid; - int j, fd; int status; - int res = 0; + int j, max_swapfiles, used_swapfiles; char filename[FILENAME_MAX]; - char buf[BUFSIZ + 1]; - /* Find out how many swapfiles (1 line per entry) already exist */ - swapfiles = 0; - - if (seteuid(0) < 0) - tst_brk(TFAIL | TERRNO, "Failed to call seteuid"); - - /* This includes the first (header) line */ - if ((fd = open("/proc/swaps", O_RDONLY)) == -1) { - tst_brk(TFAIL | TERRNO, - "Failed to find out existing number of swap files"); - } - do { - char *p = buf; - res = read(fd, buf, BUFSIZ); - if (res < 0) { - tst_brk(TFAIL | TERRNO, - "Failed to find out existing number of swap files"); - } - buf[res] = '\0'; - while ((p = strchr(p, '\n'))) { - p++; - swapfiles++; - } - } while (BUFSIZ <= res); - close(fd); - if (swapfiles) - swapfiles--; /* don't count the /proc/swaps header */ - - if (swapfiles < 0) - tst_brk(TFAIL, "Failed to find existing number of swapfiles"); + SAFE_SETEUID(0); /* Determine how many more files are to be created */ - swapfiles = MAX_SWAPFILES - swapfiles; - if (swapfiles > MAX_SWAPFILES) - swapfiles = MAX_SWAPFILES; + max_swapfiles = tst_max_swapfiles(); + used_swapfiles = tst_count_swaps(); + swapfiles = max_swapfiles - used_swapfiles; + if (swapfiles > max_swapfiles) + swapfiles = max_swapfiles; + pid = SAFE_FORK(); if (pid == 0) { /*create and turn on remaining swapfiles */ for (j = 0; j < swapfiles; j++) { - /* prepare filename for the iteration */ - if (sprintf(filename, "swapfile%02d", j + 2) < 0) { - printf("sprintf() failed to create " - "filename"); - exit(1); - } - /* Create the swapfile */ - make_swapfile(filename, 0); + snprintf(filename, sizeof(filename), "%s%02d", TEST_FILE, j + 2); + MAKE_SMALL_SWAPFILE(filename); /* turn on the swap file */ - res = tst_syscall(__NR_swapon, filename, 0); - if (res != 0) { - if (errno == EPERM) { - printf("Successfully created %d swapfiles\n", j); - break; - } else { - printf("Failed to create swapfile: %s\n", filename); - exit(1); - } - } + TST_EXP_PASS_SILENT(swapon(filename, 0)); } exit(0); } else waitpid(pid, &status, 0); if (WEXITSTATUS(status)) - tst_brk(TFAIL, "Failed to setup swaps"); + tst_brk(TFAIL, "Failed to setup swap files"); - /* Create all needed extra swapfiles for testing */ - for (j = 0; j < testfiles; j++) - make_swapfile(swap_testfiles[j].filename, 0); - - return 0; -} - -/* - * Turn off all swapfiles previously turned on - */ -static int clean_swap(void) -{ - int j; - char filename[FILENAME_MAX]; - - for (j = 0; j < swapfiles; j++) { - if (snprintf(filename, sizeof(filename), - "swapfile%02d", j + 2) < 0) { - tst_res(TWARN, "sprintf() failed to create filename"); - tst_res(TWARN, "Failed to turn off swap files. System" - " reboot after execution of LTP test" - " suite is recommended"); - return -1; - } - if (check_and_swapoff(filename) != 0) { - tst_res(TWARN, "Failed to turn off swap file %s.", filename); - return -1; - } - } - - for (j = 0; j < testfiles; j++) { - if (check_and_swapoff(swap_testfiles[j].filename) != 0) { - tst_res(TWARN, "Failed to turn off swap file %s.", - swap_testfiles[j].filename); - return -1; - } - } + tst_res(TINFO, "Successfully created %d swap files", swapfiles); + MAKE_SMALL_SWAPFILE(TEST_FILE); return 0; } @@ -225,38 +74,47 @@ static int check_and_swapoff(const char *filename) char cmd_buffer[256]; int rc = -1; - if (snprintf(cmd_buffer, sizeof(cmd_buffer), - "grep -q '%s.*file' /proc/swaps", filename) < 0) { - tst_res(TWARN, "sprintf() failed to create the command string"); - } else { + snprintf(cmd_buffer, sizeof(cmd_buffer), "grep -q '%s.*file' /proc/swaps", filename); - rc = 0; - - if (system(cmd_buffer) == 0) { - - /* now we need to swapoff the file */ - if (tst_syscall(__NR_swapoff, filename) != 0) { - - tst_res(TWARN, "Failed to turn off swap " - "file. system reboot after " - "execution of LTP test suite " - "is recommended"); - rc = -1; - - } - - } + if (system(cmd_buffer) == 0 && swapoff(filename) != 0) { + tst_res(TWARN, "Failed to swapoff %s", filename); + rc = -1; } return rc; } +/* + * Turn off all swapfiles previously turned on + */ +static void clean_swap(void) +{ + int j; + char filename[FILENAME_MAX]; + + for (j = 0; j < swapfiles; j++) { + snprintf(filename, sizeof(filename), "%s%02d", TEST_FILE, j + 2); + check_and_swapoff(filename); + } + + check_and_swapoff("testfile"); +} + +static void verify_swapon(void) +{ + TST_EXP_FAIL(swapon(TEST_FILE, 0), EPERM, "swapon(%s, 0)", TEST_FILE); +} + static void setup(void) { if (access("/proc/swaps", F_OK)) tst_brk(TCONF, "swap not supported by kernel"); - is_swap_supported("./tstswap"); + is_swap_supported(TEST_FILE); + if (setup_swap() < 0) { + clean_swap(); + tst_brk(TBROK, "Setup failed, quitting the test"); + } } static void cleanup(void) @@ -265,8 +123,10 @@ static void cleanup(void) } static struct tst_test test = { + .mntpoint = MNTPOINT, + .mount_device = 1, + .all_filesystems = 1, .needs_root = 1, - .needs_tmpdir = 1, .forks_child = 1, .test_all = verify_swapon, .setup = setup, diff --git a/testcases/kernel/syscalls/swapon/swaponoff.h b/testcases/kernel/syscalls/swapon/swaponoff.h deleted file mode 100755 index e3eae3fe..00000000 --- a/testcases/kernel/syscalls/swapon/swaponoff.h +++ /dev/null @@ -1,18 +0,0 @@ - -#ifndef __SWAP_ON_OFF_H_ -#define __SWAP_ON_OFF_H_ - -/* - * Read swapon(2) / swapoff(2) for a full history lesson behind the value of - * MAX_SWAPFILES. - */ -#include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) -#define MAX_SWAPFILES 30 -#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 10) -#define MAX_SWAPFILES 32 -#else -#define MAX_SWAPFILES 8 -#endif - -#endif diff --git a/testcases/kernel/syscalls/symlink/.gitignore b/testcases/kernel/syscalls/symlink/.gitignore index d1497e68..6d7978ed 100755 --- a/testcases/kernel/syscalls/symlink/.gitignore +++ b/testcases/kernel/syscalls/symlink/.gitignore @@ -1,5 +1,3 @@ -/symlink01 /symlink02 /symlink03 /symlink04 -/symlink05 diff --git a/testcases/kernel/syscalls/symlink/symlink01.c b/testcases/kernel/syscalls/symlink/symlink01.c deleted file mode 100755 index 8cf0c8f1..00000000 --- a/testcases/kernel/syscalls/symlink/symlink01.c +++ /dev/null @@ -1,1886 +0,0 @@ -/* - * 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: symlink01.c,v 1.20 2009/11/02 13:57:19 subrata_modak Exp $ */ -/* - * OS Test - Silicon Graphics, Inc. - * - * TEST IDENTIFIER : symlink01 (symlink) - * TEST TITLE : Make a Symbolic Link to a File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 5 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : readlink01 (readlink) - * TEST TITLE : Reads Value of a Symbolic Link - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 4 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : stat04 (stat) - * TEST TITLE : Gets File Status Indirectly From a Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : lstat01 (lstat) - * TEST TITLE : Get file Status About a Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : mkdir05 (mkdir) - * TEST TITLE : Fail When Making a Directory File Indirectly From - * a Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 1 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : rmdir03 (rmdir) - * TEST TITLE : Fail When Removing a Directory File Indirectly - * From a Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 1 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : chdir01 (chdir) - * TEST TITLE : Changes Current Working DIrectory Location - * Indirectly From a Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : link01 (link) - * TEST TITLE : Creates a Link To a File Indirectly From a - * Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : unlink01 (unlink) - * TEST TITLE : Removes a Link To a File And Not Any Object File - * Which Maybe Pointed At - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 1 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : chmod01 (chmod) - * TEST TITLE : Change Object File Permissions Indirectly From a - * Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : utime01 (utime) - * TEST TITLE : Set File Access And Modify Object File Times - * Indirectly From a Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : rename01 (rename) - * TEST TITLE : Rename a Symbolic Link File And Not Any Object - * File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 3 - * WALL CLOCK TIME : 3 - * - * TEST IDENTIFIER : open01 (open) - * TEST TITLE : Create/Open a File For Reading Or Writing - * Indirectly From a Symbolic Link File - * PARENT DOCUMENT : symtds01 - * TEST CASE TOTAL : 5 - * WALL CLOCK TIME : 3 - * - * - * EXECUTED BY : whom ever - * CPU TYPES : ALL - * AUTHOR : David Fenner - * CO-PILOT : Jon Hendrickson - * DATE STARTED : 07/25/90 - * INITIAL RELEASE : UNICOS 6.0 - * - * TEST CASES - * - * For symlink - * 1. Create symbolic link with abnormal object name path - * 2. Create symbolic link with normal object name path - * 3. Create symbolic link with path to an existing object file - * 4. Receive EEXIST error when creating an already existing symbolic link file. - * 5. Receive ENAMETOOLONG error when creating symbolic link which exceeds PATH_MAX in length - * - * For readlink - * 1. Read a symbolic link file which points at no object file - * 2. Read a symbolic link file which points at an object file - * 3. Receive ENAMETOOLONG error when reading symbolic link which exceeds PATH_MAX in length - * 4. Receive an EINVAL error when reading a file which is not a symbolic - * link file. - * - * For stat - * 1. Get object file status through symbolic link file - * 2. Receive ENOENT error when accessing non-existent object file through symbolic link file - * 3. Receive ELOOP error when nesting of symbolic links exceed maximum - * - * For lstat - * 1. Get symbolic link file status when pointing at no object file - * 2. Get symbolic link file status when pointing at an object file - * 3. Get object file status when argument is not a symbolic link - * file. - * - * For mkdir - * 1. Receive EEXIST error when creating a directory through a symbolic link file - * - * For rmdir - * 1. Receive ENOTDIR error when removing an existing directory through a symbolic link file - * - * For chdir - * 1. Change current working directory through a symbolic link file - * 2. Receive ENOENT error when accessing non-existent directory through symbolic link file - * 3. Receive ELOOP error when nesting of symbolic links exceed maximum - * - * For link - * 1. Link an object file to a new file through symbolic link file - * 2. Receive ENOENT error when accessing non-existent object file through symbolic link file - * 3. Receive ELOOP error when nesting of symbolic links exceed maximum - * - * For unlink - * 1. Delete a symbolic link file and not the object file which it points at - * - * For chmod - * 1. Change file permissions of object file through a symbolic link file - * 2. Receive ENOENT error when accessing non-existent directory through symbolic link file - * 3. Receive ELOOP error when nesting of symbolic links exceed maximum - * - * For utime - * 1. Change inode times of object file through a symbolic link file - * 2. Receive ENOENT error when accessing non-existent directory through symbolic link file - * 3. Receive ELOOP error when nesting of symbolic links exceed maximum - * - * For rename - * 1. Rename a symbolic link file which points at no object file - * 2. Rename a symbolic link file which points at an object file without any object file alterations. - * 3. Receive EXDEV when trying to rename a symbolic link file to an address outside of current file system - * - * For open - * 1. Create an object file through a symbolic link file - * 2. Open an object file through a symbolic link file - * 3. Receive EEXIST error when exclusively creating an object file through a symbolic link file - * 4. Receive ENOENT error when accessing non-existent object file through symbolic link file - * 5. Receive ELOOP error when nesting of symbolic links exceed maximum - * - * ENVIRONMENTAL NEEDS - * None - * - * DETAILED DESCRIPTION - * - * Self-documenting code so see below - */ - -#include -#include -#include -#include /* open(2) system call */ -#include -#include -#include /* utime(2) system call */ -#include -#include /* stat(2) and lstat(2) system calls */ -#include -#include - -#include "test.h" - -void setup(void); -void cleanup(void); -void help(void); -void delete_files(char *path1, char *path2); -struct all_test_cases; -void do_EEXIST(struct all_test_cases *tc_ptr); -void do_ENOENT(struct all_test_cases *tc_ptr); -void do_ELOOP(struct all_test_cases *tc_ptr); -void do_ENOTDIR(struct all_test_cases *tc_ptr); -void do_EXDEV(struct all_test_cases *tc_ptr); -void do_ENAMETOOLONG(struct all_test_cases *tc_ptr); -void do_EINVAL(struct all_test_cases *tc_ptr); -void do_readlink(struct all_test_cases *tc_ptr); -void do_stat(struct all_test_cases *tc_ptr); -void do_chdir(struct all_test_cases *tc_ptr); -void do_link(struct all_test_cases *tc_ptr); -void do_unlink(struct all_test_cases *tc_ptr); -void do_chmod(struct all_test_cases *tc_ptr); -void do_utime(struct all_test_cases *tc_ptr); -void do_rename(struct all_test_cases *tc_ptr); -void do_open(struct all_test_cases *tc_ptr); -struct tcses; -int do_syscalltests(struct tcses *tcs); -struct tcses *get_tcs_info(char *ptr); - -#define S_FILE "symbolic" /* Name of symbolic link file */ -#define O_FILE "object" /* Name of object file */ -#define A_S_FILE "asymbolic" /* Another name for a symbolic link file */ -#define Y_A_S_FILE "/NiCkEr" /* Yet another symbolic link file */ -#define BIG_STRING "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" - -#define DEFAULT_TCID "symlink01" - -#define SYMLINK "symlink01" -#define READLINK "readlink01" -#define STAT "stat04" -#define STAT_64 "stat04_64" -#define LSTAT "lstat01" -#define LSTAT_64 "lstat01_64" -#define MKDIR "mkdir05" -#define RMDIR "rmdir03" -#define CHDIR "chdir01" -#define LINK "link01" -#define UNLINK "unlink01" -#define CHMOD "chmod01" -#define UTIME "utime01" -#define RENAME "rename01" -#define OPEN "open01" - -#define cktcsid(s1,s2) (!strcmp(s1,s2)) -#define BUFMAX 512 -#define MODE 0700 -#define MASK 0100777 /* A regular file with r,w,x for all mask */ - -/* - * Lets be optimistic and only define messages for passing test cases - */ -const char *msgs[] = { - "Creation of symbolic link file to no object file is ok", - "Creation of symbolic link file and object file via symbolic link is ok", - "Creating an existing symbolic link file error is caught", - "Creating a symbolic link which exceeds maximum pathname error is caught", - "Reading of symbolic link file contents checks out ok", - "Reading a symbolic link which exceeds maximum pathname error is caught", - "Getting stat info about object file through symbolic link file is ok", - "Stat(2) error when accessing non-existent object through symbolic link is caught", - "lstat(2) of symbolic link file which points to no object file is ok", - "lstat(2) of symbolic link file which points at an object file is ok", - "mkdir(2) of object file through symbolic link file failed as expected", - "rmdir(2) of object file through symbolic link file failed as expected", - "chdir(2) to object file location through symbolic link file is ok", - "chdir(2) to non-existent object file location through symbolic link file failed as expected", - "link(2) to a symbolic link, which is pointing to an existing object file worked - file created and link count adjusted", - "link(2) to a symbolic link, which is pointing to a non-existing object file worked ok - file created and link count adjusted.", - "unlink(2) of symbolic link file with no object file removal is ok", - "chmod(2) of object file permissions through symbolic link file is ok", - "chmod(2) error when accessing non-existent object through symbolic link is caught", - "utime(2) change of object file access and modify times through symbolic link file is ok", - "utime(2) error when accessing non-existent object through symbolic link is caught", - "rename(3) of symbolic link file name which points at no object file is ok", - "rename(3) of symbolic link file name which points at object file is ok", - "rename(3) error of symbolic link file name across file systems is caught", - "open(2) with (O_CREAT | O_RDWR) to create object file through symbolic link file and all writes, reads, and lseeks are ok", - "open(2) with O_RDWR of existing object file through symbolic link file and all writes, reads, and lseeks are ok", - "open(2) with (O_CREAT | O_EXCL) error is caught when creating object file through symbolic link file", - "open(2) error with O_RDWR is caught when processing symbolic link file which points at no object file", - "Nested symbolic link access condition caught. ELOOP is returned", - "Reading a nonsymbolic link file error condition is caught. EINVAL is returned", - "lstat(2) of object file returns object file inode information", - "NULL" -}; - -/* - * Define test object setup and validation functions - */ -int creat_both(char *path1, char *path2, char *path3); -int creat_symlink(char *path1, char *path2, char *_path3); -int creat_path_max(char *path1, char *path2, char *path3); -int ck_symlink(char *path1, char *path2, char *path3); -int creat_object(char *path1, char *_path2, char *_path3); -int ck_object(char *path1, char *path2, char *path3); -int ck_both(char *path1, char *path2, char *path3); -int ck_path_max(char *path1, char *path2, char *path3); - -/* - * Define test cases - */ -struct all_test_cases { - char *tcid; - int test_fail; - int errno_val; - int pass_msg; - int (*test_setup) (char *path1, char *path2, char *path3); - int (*ck_test) (char *path1, char *path2, char *path3); - char *fn_arg[3]; - -} test_objects[] = { - { - SYMLINK, 0, 0, 0, creat_symlink, ck_symlink, { - "%bc+eFhi!k", S_FILE, NULL}}, { - SYMLINK, 0, 0, 0, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - SYMLINK, 0, 0, 1, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - SYMLINK, 1, EEXIST, 2, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - SYMLINK, 1, ENAMETOOLONG, 3, creat_path_max, ck_path_max, { - O_FILE, S_FILE, NULL}}, { - READLINK, 0, 0, 4, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - READLINK, 0, 0, 4, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - READLINK, 1, ENAMETOOLONG, 5, creat_path_max, ck_path_max, { - O_FILE, S_FILE, NULL}}, { - READLINK, 1, EINVAL, 29, creat_object, ck_object, { - O_FILE, NULL, NULL}}, { - STAT, 0, 0, 6, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, - /* 10 */ - { - STAT, 1, ENOENT, 7, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - STAT, 1, ELOOP, 28, creat_symlink, ck_symlink, { - S_FILE, S_FILE, NULL}}, { - STAT_64, 0, 0, 6, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - STAT_64, 1, ENOENT, 7, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - STAT_64, 1, ELOOP, 28, creat_symlink, ck_symlink, { - S_FILE, S_FILE, NULL}}, { - LSTAT, 0, 0, 8, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - LSTAT, 0, 0, 9, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - LSTAT, 0, 0, 30, creat_object, ck_object, { - O_FILE, NULL, NULL}}, { - LSTAT_64, 0, 0, 8, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - LSTAT_64, 0, 0, 9, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, - /* 20 */ - { - LSTAT_64, 0, 0, 30, creat_object, ck_object, { - O_FILE, NULL, NULL}}, { - MKDIR, 1, EEXIST, 10, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - RMDIR, 1, ENOTDIR, 11, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - CHDIR, 0, 0, 12, creat_symlink, ck_symlink, { - O_FILE, S_FILE, O_FILE}}, { - CHDIR, 1, ENOENT, 13, creat_symlink, ck_symlink, { - "%bc+eFhi!k", S_FILE, NULL}}, { - CHDIR, 1, ELOOP, 28, creat_symlink, ck_symlink, { - S_FILE, S_FILE, NULL}}, { - LINK, 0, 0, 14, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - LINK, 0, 0, 15, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, - /* The following link test case is invalid - leaving it defined so */ - /* I don't have to change all the entries in the all_tcses array after link */ - /* It has been disabled at the moment. */ - { - LINK, 1, -1, -1, creat_symlink, ck_symlink, { - NULL, NULL, NULL}}, { - UNLINK, 0, 0, 16, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, - /* 30 */ - { - CHMOD, 0, 0, 17, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - CHMOD, 1, ENOENT, 18, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - CHMOD, 1, ELOOP, 28, creat_symlink, ck_symlink, { - S_FILE, S_FILE, NULL}}, { - UTIME, 0, 0, 19, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - UTIME, 1, ENOENT, 20, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - UTIME, 1, ELOOP, 28, creat_symlink, ck_symlink, { - S_FILE, S_FILE, NULL}}, { - RENAME, 0, 0, 21, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - RENAME, 0, 0, 22, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, - /* The following rename test makes assumption that the link and target */ - /* files are located in different filesystems, which is incorrect. */ - /* It has been disabled at the moment. */ - { - RENAME, 1, EXDEV, 23, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - OPEN, 0, 0, 24, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, - /* 40 */ - { - OPEN, 0, 0, 25, creat_both, ck_both, { - O_FILE, S_FILE, O_FILE}}, { - OPEN, 1, EEXIST, 26, creat_symlink, ck_symlink, { - O_FILE, S_FILE, O_FILE}}, { - OPEN, 1, ENOENT, 27, creat_symlink, ck_symlink, { - O_FILE, S_FILE, NULL}}, { - OPEN, 1, ELOOP, 28, creat_symlink, ck_symlink, { - S_FILE, S_FILE, NULL}} -}; - -/* - * Define tcses - */ -struct tcses { - char *tcid; - char *syscall; - int test_cases; /* number of entries in test_objects array */ - struct all_test_cases *tc_ptr; - char *desc; -} all_tcses[] = { - - { - SYMLINK, "symlink", 5, &test_objects[0], - "Make a Symbolic Link to a File"}, { - READLINK, "readlink", 4, &test_objects[5], - "Reads Value of a Symbolic Link"}, { - STAT, "stat", 3, &test_objects[9], - "Gets File Status Indirectly From a Symbolic Link file"}, { - STAT_64, "stat64", 3, &test_objects[12], - "Gets File Status Indirectly From a Symbolic Link file"}, { - LSTAT, "lstat", 3, &test_objects[15], - "Get file Status About a Symbolic Link File"}, { - LSTAT_64, "lstat64", 3, &test_objects[18], - "Get file Status About a Symbolic Link File"}, { - MKDIR, "mkdir", 1, &test_objects[21], - "Fail When Making a Directory File Indirectly from a symlink"}, - { - RMDIR, "rmdir", 1, &test_objects[22], - "Fail When Removing a Directory File Indirectly from a symlink"}, - { - CHDIR, "chdir", 3, &test_objects[23], - "Changes CWD Location Indirectly from a symlink"}, { - LINK, "link", 2, &test_objects[26], - "Creates a Link To a File Indirectly From a Symbolic"}, { - UNLINK, "unlink", 1, &test_objects[29], - "Removes a Link To a File but not the Object File"}, { - CHMOD, "chmod", 3, &test_objects[30], - "Change Object File Permissions Indirectly From a Symbolic"}, - { - UTIME, "utime", 3, &test_objects[33], - "Set File Access And Modify Object File Times via symlink"}, - { - RENAME, "rename", 2, &test_objects[36], - "Rename a Symbolic Link File And Not Any Object file"}, { -OPEN, "open", 5, &test_objects[39], - "Create/Open a File For Reading Or Writing via symlink"},}; - -/* - * Define GLOBAL variables - */ - -int TST_TOTAL; -int TEST_RESULT; -time_t a_time_value = 100; -char *TCID; -char *Selectedtests = NULL; /* Name (tcid) of selected test cases */ -char test_msg[BUFMAX]; -char full_path[PATH_MAX + 1 + 1]; /* Add one for '\0' and another to exceed the PATH_MAX limit, see creat_path_max() */ - -struct stat asymlink, statter; -char Buffer[1024]; -char Buf[1024]; - -char *Tcid = NULL; - -option_t Options[] = { - {"T:", NULL, &Tcid}, /* -T tcid option */ - {NULL, NULL, NULL} -}; - -/*********************************************************************** - * MAIN - ***********************************************************************/ -int main(int argc, char *argv[]) -{ - struct tcses *tcs_ptr; - int lc; - - tst_parse_opts(argc, argv, Options, &help); - - /* - * If the -T option was used, use that TCID or use the default - */ - if (Tcid != NULL) { - TCID = Tcid; - Selectedtests = Tcid; - - } -#ifndef ALL - else { - TCID = DEFAULT_TCID; - Selectedtests = DEFAULT_TCID; - } -#endif - - /* - * Get test case specification information and assign TST_TOTAL - */ - if ((tcs_ptr = get_tcs_info(Selectedtests)) == NULL) { - TST_TOTAL = 1; - tst_brkm(TBROK, cleanup, - "Unknown symbolic link test case specification executed"); - } - - /*************************************************************** - * perform global setup for test - ***************************************************************/ - - setup(); - - /*************************************************************** - * check looping state if -c option given - ***************************************************************/ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Execute tcs testing function and all defined test cases - */ - do_syscalltests(tcs_ptr); - - } - - /* - * End appropriately - */ - cleanup(); - tst_exit(); - -} - -/*********************************************************************** - * This function maps the name of the process to a test case specification - * defined in the all_tcses array of tcses structures. Either a pointer - * to the mapped test case specification information is returned or a - * null pointer. - * - * Argument is path to program name. - ***********************************************************************/ -struct tcses *get_tcs_info(char *ptr) -{ - int ctr; - struct tcses *tcs_ptr; - -#if ALL - if (ptr == NULL) { - - TST_TOTAL = 0; - for (ctr = 1; ctr < sizeof(all_tcses) / sizeof(struct tcses); - ctr++) - TST_TOTAL += all_tcses[ctr].test_cases; - return all_tcses; - } -#endif - - for (ctr = 0; ctr < (int)(sizeof(all_tcses) / sizeof(struct tcses)); ctr++) { - if (strcmp(ptr, all_tcses[ctr].tcid) == 0 || - strcmp(ptr, all_tcses[ctr].syscall) == 0) { - tcs_ptr = &all_tcses[ctr]; - TCID = all_tcses[ctr].tcid; - TST_TOTAL = tcs_ptr->test_cases; - return (tcs_ptr); - } - - } - return NULL; -} - -/*********************************************************************** - * Determines if what path points at is a symbolic link file - * - * Argument is path to symbolic link file. - * - * Return status is one if a symbolic link file. Zero if not a symbolic - * link file and a minus one if the path doesn't point at a file. - ***********************************************************************/ -static int see_if_a_symlink(char *path) -{ - if (lstat(path, &asymlink) < 0) - return (-1); - - if ((asymlink.st_mode & S_IFMT) == S_IFLNK) - return 1; - else - return 0; -} - -/*********************************************************************** - * This function performs without any hesitation, file(s) deletions - ***********************************************************************/ -void delete_files(char *path1, char *path2) -{ - unlink(path1); - unlink(path2); -} - -/*********************************************************************** - * - * This routine creates a symbolic link file. - * - * Argument one is symbolic link pathname to point at. - * Argument two is name of symbolic link file. - * - ***********************************************************************/ -int creat_symlink(char *path1, char *path2, char *_path3) -{ - TEST(symlink(path1, path2)); - errno = TEST_ERRNO; - if (TEST_RETURN == -1) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "symlink(2) Failure when creating setup %s object file: errno:%d %s", - path1, errno, strerror(errno)); - return 0; - } else { - sprintf(Buf, "symlink(%s, %s) was successful.\n", path1, path2); - strcat(Buffer, Buf); -#if DEBUG - tst_resm(TPASS, "symlink(%s, %s) was successful.", path1, path2); -#endif - } - return 1; -} -#define creat_symlink(p1, p2) creat_symlink(p1, p2, NULL) - -/*********************************************************************** - * - * This routine creates a regular file. - * - * Argument one is a pathname - * - ***********************************************************************/ -int creat_object(char *path1, char *_path2, char *_path3) -{ - int fd; - if ((fd = creat(path1, MODE)) == -1) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "creat(2) Failure when creating setup %s object file: errno:%d %s", - path1, errno, strerror(errno)); - return 0; - } else { - sprintf(Buf, "creat(%s, %#o) was successful.\n", path1, MODE); - strcat(Buffer, Buf); -#if DEBUG - tst_resm(TPASS, "creat(%s, %#o) was successful.", path1, MODE); -#endif - } - if (close(fd) == -1) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "close(2) Failure when closing setup %s object file: errno:%d %s", - path1, errno, strerror(errno)); - return 0; - } - return 1; -} -#define creat_object(p1) creat_object(p1, NULL, NULL) - -/*********************************************************************** - * - * This routine creates a symbolic link file and a regular file. - * - * Argument one is a pathname of object file - * Argument two is symbolic link file name - * Argument three is regular file name - * - ***********************************************************************/ -int creat_both(char *path1, char *path2, char *path3) -{ - if (creat_symlink(path1, path2) == -1) - return 0; - else if (creat_object(path3) == -1) - return 0; - return 1; -} - -/*********************************************************************** - * - * This routine checks if symbolic link file is a symbolic link file. - * - * Argument one is a pathname of object file - * Argument two is symbolic link file name - * Argument three is regular file name - * - ***********************************************************************/ -int ck_symlink(char *path1, char *path2, char *path3) -{ - int ret; - - if ((ret = see_if_a_symlink(path2)) == -1) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "lstat(2) Failure when accessing %s symbolic link file which should contain %s path to %s file ", - path2, path1, path3); - return 0; - } else if (ret == 0) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "%s is not a symbolic link file which contains %s path to %s file", - path2, path1, path3); - return 0; - } - return 1; -} - -/*********************************************************************** - * - * This routine checks if symbolic link file points at object file. - * - * Argument one is a pathname of object file - * Argument two is symbolic link file name - * Argument three is regular file name - * - ***********************************************************************/ -int ck_both(char *path1, char *path2, char *path3) -{ - if (ck_symlink(path1, path2, path3) == 0) - return 0; - else if ((stat(path3, &statter) == -1) && (errno == ENOENT)) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "stat(2) Failure when accessing %s object file ", - path3); - return 0; - } else if ((stat(path2, &asymlink) == -1) && (errno == ENOENT)) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "stat(2) Failure when accessing %s symbolic link file ", - path2); - return 0; - } else if (statter.st_ino != asymlink.st_ino) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "stat(2) Failure when accessing %s object file through %s symbolic link file ", - path3, path2); - return 0; - } - return 1; - -} - -/*********************************************************************** - * This routine populates full_path with a pathname whose length exceeds - * the PATH_MAX define value in param.h - * - * Argument one is a pathname of object file - * Argument two is symbolic link file name - * Argument three is regular file name - ***********************************************************************/ -int creat_path_max(char *path1, char *path2, char *path3) -{ - int ctr, to_go, size, whole_chunks; - char *cwd; - - if ((cwd = getcwd(NULL, 0)) == NULL) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "getcwd(3) Failure in setup of %s %s %s test case object elements", - path1, path2, path3); - return 0; - } - cwd = getcwd(NULL, 0); - size = strlen(cwd); - - to_go = PATH_MAX - size; - size = strlen(path1); - whole_chunks = to_go / size; - strcpy(full_path, cwd); - for (ctr = 0; ctr < whole_chunks; ctr++) { - strcat(full_path, path1); - } - size = strlen(full_path); - to_go = PATH_MAX - size; - strcat(full_path, "/"); - for (ctr = 0; ctr < to_go; ctr++) - strcat(full_path, "Z"); - - return 1; -} - -/*********************************************************************** - * This routine checks that full_path's length exceeds the PATH_MAX - * define value in param.h - * - * Argument one is a pathname of object file - * Argument two is symbolic link file name - * Argument three is regular file name - ***********************************************************************/ -int ck_path_max(char *path1, char *path2, char *path3) -{ - if (strlen(full_path) == (PATH_MAX + 1)) - return 1; - else { - TEST_RESULT = TBROK; - sprintf(test_msg, "%s %d %s %s %s %s", - "full_path character array length was not", - (PATH_MAX + 1), - "characters long for test case object elements", path1, - path2, path3); - return 0; - } -} - -/*********************************************************************** - * This routine checks if the stat(2) and lstat(2) calls return the same - * information when the path is not a symbolic link file - * - * Argument one is a pathname of object file - * Argument two is symbolic link file name - * Argument three is regular file name - * - ***********************************************************************/ -int ck_object(char *path1, char *path2, char *path3) -{ - int ret; - - if ((ret = see_if_a_symlink(path1)) < 0) { - TEST_RESULT = TFAIL; - sprintf(test_msg, - "lstat(2) failed to return inode information for a regular object file"); - return 0; - } else if (ret == 1) { - TEST_RESULT = TFAIL; - sprintf(test_msg, - "lstat(2) detected a regular object file as a symbolic link file"); - return 0; - } else if (stat(path1, &statter) == -1) { - TEST_RESULT = TBROK; - sprintf(test_msg, - "stat(2) failed to return inode information for a regular object file"); - return 0; - } else if (memcmp((char *)&statter, (char *)&asymlink, sizeof(statter)) - != 0) { - TEST_RESULT = TFAIL; - sprintf(test_msg, - "lstat(2) and stat(2) do not return same inode information for an object file"); - return 0; - - } - return 1; -} - -/*********************************************************************** - * Main test case processing function - * - * Argument is a ptr into the all_tcses array of structures of type tcses - ***********************************************************************/ -int do_syscalltests(struct tcses *tcs) -{ - int ctr, ret; - struct all_test_cases *tc_ptr; - - /* - * loop through desired number of test cases - */ - for (ctr = 0, tc_ptr = tcs->tc_ptr; ctr < TST_TOTAL; ctr++, tc_ptr++) { - - Buffer[0] = '\0'; - - /* - * If running all test cases for all tcid, set the TCID if needed. - */ - if (Selectedtests == NULL) { - if (strcmp(tcs->tcid, tc_ptr->tcid) != 0) { - TCID = tc_ptr->tcid; - tst_count = 0; - } - } - /* - * Insure that we are executing the correct tcs test case - */ - if (strcmp(tcs->tcid, tc_ptr->tcid) != 0) { - tst_resm(TBROK, - "%s TCID attempted to execute %s %d %d test case", - tcs->tcid, tc_ptr->tcid, tc_ptr->test_fail, - tc_ptr->errno_val); - continue; - } - TEST_RESULT = TPASS; - delete_files(S_FILE, O_FILE); - /* - * Perform test case setup - */ - ret = - (tc_ptr->test_setup) (tc_ptr->fn_arg[0], tc_ptr->fn_arg[1], - tc_ptr->fn_arg[2]); - - /* If an expected error, try it out */ - - if (tc_ptr->test_fail) { - /* - * Try to perform test verification function - */ - if (!(tc_ptr->ck_test) - (tc_ptr->fn_arg[0], tc_ptr->fn_arg[1], - tc_ptr->fn_arg[2])) - tst_resm(TEST_RESULT, "%s", test_msg); - else if (tc_ptr->errno_val == EEXIST) - do_EEXIST(tc_ptr); - else if (tc_ptr->errno_val == ENOENT) - do_ENOENT(tc_ptr); - else if (tc_ptr->errno_val == ELOOP) - do_ELOOP(tc_ptr); - else if (tc_ptr->errno_val == ENOTDIR) - do_ENOTDIR(tc_ptr); - else if (tc_ptr->errno_val == EXDEV) - do_EXDEV(tc_ptr); - else if (tc_ptr->errno_val == ENAMETOOLONG) - do_ENAMETOOLONG(tc_ptr); - else if (tc_ptr->errno_val == EINVAL) - do_EINVAL(tc_ptr); - else - tst_resm(TBROK, "Test Case Declaration Error"); - } else if (ret == 1) { /* No setup function error */ - - if (tc_ptr->errno_val != 0) - tst_resm(TBROK, "Test Case Declaration Error"); - else { - /* - * Perform test verification function - */ - ret = - (tc_ptr->ck_test) (tc_ptr->fn_arg[0], - tc_ptr->fn_arg[1], - tc_ptr->fn_arg[2]); - - /* Perform requested symbolic link system call test */ - - if ((cktcsid(tc_ptr->tcid, SYMLINK)) || - (cktcsid(tc_ptr->tcid, LSTAT)) || - (cktcsid(tc_ptr->tcid, LSTAT_64))) { - if (ret == 1) - tst_resm(TEST_RESULT, "%s", - msgs[tc_ptr-> - pass_msg]); - else - tst_resm(TEST_RESULT, "%s", - test_msg); - } else if (ret == 0) - tst_resm(TEST_RESULT, "%s", test_msg); - else if (cktcsid(tc_ptr->tcid, READLINK)) - do_readlink(tc_ptr); - else if (cktcsid(tc_ptr->tcid, STAT)) - do_stat(tc_ptr); - else if (cktcsid(tc_ptr->tcid, STAT_64)) - do_stat(tc_ptr); - else if (cktcsid(tc_ptr->tcid, CHDIR)) - do_chdir(tc_ptr); - else if (cktcsid(tc_ptr->tcid, LINK)) - do_link(tc_ptr); - else if (cktcsid(tc_ptr->tcid, UNLINK)) - do_unlink(tc_ptr); - else if (cktcsid(tc_ptr->tcid, CHMOD)) - do_chmod(tc_ptr); - else if (cktcsid(tc_ptr->tcid, UTIME)) - do_utime(tc_ptr); - else if (cktcsid(tc_ptr->tcid, RENAME)) - do_rename(tc_ptr); - else if (cktcsid(tc_ptr->tcid, OPEN)) - do_open(tc_ptr); - else - tst_resm(TBROK, - "Unknown test case processing actions declared"); - } - } else - tst_resm(TBROK, "Test Case Declaration Error"); - } - return 0; -} - -/*********************************************************************** - * This routine checks for the return of EEXIST errno from requested - * system call - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_EEXIST(struct all_test_cases *tc_ptr) -{ - if (cktcsid(tc_ptr->tcid, SYMLINK)) { - - TEST(symlink(tc_ptr->fn_arg[0], tc_ptr->fn_arg[1])); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == EEXIST)) - tst_resm(TPASS, "%s", msgs[tc_ptr->pass_msg]); - else - tst_resm(TFAIL, "%s %s", - "Expected EEXIST error when creating a symbolic link file", - "which already existed"); - } else if (cktcsid(tc_ptr->tcid, MKDIR)) { - - TEST(mkdir(tc_ptr->fn_arg[1], MODE)); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == EEXIST)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - - tst_resm(TFAIL, "%s %s", - "Expected EEXIST error when creating a directory by a symbolic", - "link file which pointed at no object file"); - rmdir(tc_ptr->fn_arg[1]); - } - } else if (cktcsid(tc_ptr->tcid, OPEN)) { - - TEST(open(tc_ptr->fn_arg[1], (O_EXCL | O_CREAT), 0666)); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == EEXIST)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected EEXIST error for exclusively opening an object file", - "through a symbolic link file was not received:", - errno, strerror(errno)); - } - } else - tst_resm(TBROK, - "Unknown test case processing actions declared"); -} - -/*********************************************************************** - * This routine checks for the return of ENOENT errno from requested - * system call - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_ENOENT(struct all_test_cases *tc_ptr) -{ - if ((cktcsid(tc_ptr->tcid, STAT)) || (cktcsid(tc_ptr->tcid, STAT_64))) { - - if ((stat(tc_ptr->fn_arg[1], &asymlink) == -1) - && (errno == ENOENT)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected ENOENT error for stating a non-existent directory", - "through a symbolic link file was not received:", - errno, strerror(errno)); - } - } else if (cktcsid(tc_ptr->tcid, CHDIR)) { - if ((chdir(tc_ptr->fn_arg[1]) == -1) && (errno == ENOENT)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected ENOENT error for changing to a non-existent", - "directory through a symbolic link file was not received:", - errno, strerror(errno)); - /* FIXME (garrcoop): memory leak */ - chdir(tst_get_tmpdir()); - } - } else if (cktcsid(tc_ptr->tcid, LINK)) { - - if ((link(tc_ptr->fn_arg[1], "nick") == -1) - && (errno == ENOENT)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected ENOENT error condition when link(2) a symbolic", - "link which pointed at no object:", errno, - strerror(errno)); - delete_files("nick", NULL); - } - } else if (cktcsid(tc_ptr->tcid, CHMOD)) { - - if ((chmod(tc_ptr->fn_arg[1], MODE) == -1) && (errno == ENOENT)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected ENOENT error condition when chmod(2) a symbolic", - "link which pointed at no object,", errno, - strerror(errno)); - } - } else if (cktcsid(tc_ptr->tcid, UTIME)) { - - if ((utime(tc_ptr->fn_arg[1], NULL) == -1) && (errno == ENOENT)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected ENOENT error condition when utime(2) a symbolic", - "link which pointed at no object:", errno, - strerror(errno)); - } - } else if (cktcsid(tc_ptr->tcid, OPEN)) { - - if ((open(tc_ptr->fn_arg[1], O_RDWR) == -1) - && (errno == ENOENT)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected ENOENT error for opening a non-existent object", - " file through a symbolic link file was not received,", - errno, strerror(errno)); - } - } else - tst_resm(TBROK, - "Unknown test case processing actions declared"); -} - -/*********************************************************************** - * This routine checks for the return of ELOOP errno from requested - * system call - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_ELOOP(struct all_test_cases *tc_ptr) -{ - if ((cktcsid(tc_ptr->tcid, STAT)) || (cktcsid(tc_ptr->tcid, STAT_64))) { - - TEST(stat(tc_ptr->fn_arg[1], &asymlink)); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == ELOOP)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TEST_RESULT, "%s errno:%d %s", - "Expected ELOOP errno from stat(2) (nested symb link),", - errno, strerror(errno)); - } - } else if (cktcsid(tc_ptr->tcid, CHDIR)) { - - TEST(chdir(tc_ptr->fn_arg[1])); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == ELOOP)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - - tst_resm(TFAIL, "%s errno:%d %s", - "Expected ELOOP error condition when chdir(2) a nested symbolic link:", - errno, strerror(errno)); - /* FIXME (garrcoop): memory leak */ - chdir(tst_get_tmpdir()); - } - } else if (cktcsid(tc_ptr->tcid, LINK)) { - - TEST(link(tc_ptr->fn_arg[1], O_FILE)); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == ELOOP)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s errno:%d %s", - "Expected ELOOP error condition when link(2) a nested symbolic link:", - errno, strerror(errno)); - } - } else if (cktcsid(tc_ptr->tcid, CHMOD)) { - - TEST(chmod(tc_ptr->fn_arg[1], MODE)); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == ELOOP)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s errno:%d %s", - "Expected ELOOP error condition when chmod(2) a nested symbolic link:", - errno, strerror(errno)); - } - return; - } else if (cktcsid(tc_ptr->tcid, UTIME)) { - - TEST(utime(tc_ptr->fn_arg[1], NULL)); - errno = TEST_ERRNO; - - if ((TEST_RETURN == -1) && (errno == ELOOP)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s errno:%d %s", - "Expected ELOOP error condition when utime(2) a nested symbolic link:", - errno, strerror(errno)); - } - } else if (cktcsid(tc_ptr->tcid, OPEN)) { - - int fd; - TEST(open(tc_ptr->fn_arg[1], O_CREAT, 0666)); - fd = TEST_RETURN; - errno = TEST_ERRNO; - if ((fd == -1) && (errno == ELOOP)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s errno:%d %s", - "Expected ELOOP error condition when open(2) a nested symbolic link:", - errno, strerror(errno)); - } - } else - tst_resm(TBROK, - "Unknown test case processing actions declared"); -} - -/*********************************************************************** - * This routine checks for the return of ENOTDIR errno from requested - * system call - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_ENOTDIR(struct all_test_cases *tc_ptr) -{ - if (cktcsid(tc_ptr->tcid, RMDIR)) { - - TEST(mkdir(tc_ptr->fn_arg[0], MODE)); - errno = TEST_ERRNO; - if (TEST_RETURN == -1) - tst_resm(TBROK, "mkdir(2) Failure when creating %s", - tc_ptr->fn_arg[0]); - else if ((rmdir(tc_ptr->fn_arg[1]) == -1) && (errno == ENOTDIR)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - rmdir(tc_ptr->fn_arg[0]); - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected ENOTDIR error for removing a non-existent", - "directory through a symbolic link file was not received,", - errno, strerror(errno)); - } - } else - tst_resm(TBROK, - "Unknown test case processing actions declared"); -} - -/*********************************************************************** - * This routine checks for the return of EXDEV errno from requested - * system call - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_EXDEV(struct all_test_cases *tc_ptr) -{ - if (cktcsid(tc_ptr->tcid, RENAME)) { - - TEST(rename(tc_ptr->fn_arg[1], Y_A_S_FILE)); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == EXDEV)) { - if (see_if_a_symlink(Y_A_S_FILE) == -1) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, - "%s %s %s file outside of current file system", - "rename(3) returned -1 when trying to move symbolic link file", - "outside of current file system, but created", - Y_A_S_FILE); - } - } else { - tst_resm(TFAIL, "%s %s errno:%d %s", - "Expected EXDEV error for renaming an existing symbolic", - "link file to a location outside of existing file system,", - errno, strerror(errno)); - delete_files("/NiCkEr", NULL); - } - } else - tst_resm(TBROK, - "Unknown test case processing actions declared"); -} - -/*********************************************************************** - * This routine checks for the return of ENAMETOOLONG errno from requested - * system call - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_ENAMETOOLONG(struct all_test_cases *tc_ptr) -{ - int ret; - - if (cktcsid(tc_ptr->tcid, SYMLINK)) { - - TEST(symlink(tc_ptr->fn_arg[0], full_path)); - errno = TEST_ERRNO; - if ((TEST_RETURN == -1) && (errno == ENAMETOOLONG)) { - if (see_if_a_symlink(full_path) == -1) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s %s %d %s", - "symlink(2) returned -1 when trying to create a symbolic", - "link file whose name exceeded", - (PATH_MAX + 1), - "characters, but it created the symbolic link file"); - } - } else { - tst_resm(TFAIL | TERRNO, - "Expected ENAMETOOLONG error when creating %s symbolic link file with a path exceeding %d characters", - tc_ptr->fn_arg[1], (PATH_MAX + 1)); - } - } else if (cktcsid(tc_ptr->tcid, READLINK)) { - - char scratch[PATH_MAX + 1]; - - ret = readlink(full_path, scratch, strlen(full_path)); - if ((ret == -1) && (errno == ENAMETOOLONG)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, - "Expected ENAMETOOLONG error when reading %s symbolic link file with a path exceeding %d characters: errno:%d %s", - tc_ptr->fn_arg[1], (PATH_MAX + 1), errno, - strerror(errno)); - } - } else - tst_resm(TBROK, - "Unknown test case processing actions declared"); -} - -/*********************************************************************** - * This routine checks for the return of EINVAL errno from requested - * system call - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_EINVAL(struct all_test_cases *tc_ptr) -{ - if (cktcsid(tc_ptr->tcid, READLINK)) { - TEST(readlink(tc_ptr->fn_arg[0], test_msg, BUFMAX)); - errno = TEST_ERRNO; - if (TEST_RETURN == -1) { - if (errno == EINVAL) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, - "readlink(2) ret:-1, errno:%d, : Exp errno:%d", - errno, EINVAL); - } - } else { - tst_resm(TFAIL, - "readlink(2) did not returned -1 when reading %s", - "a file which is not a symbolic link file"); - } - } else - tst_resm(TBROK, - "Unknown test case processing actions declared"); -} - -/*********************************************************************** - * This routine checks out the readlink(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_readlink(struct all_test_cases *tc_ptr) -{ - char scratch[PATH_MAX]; - int ret; - - ret = readlink(tc_ptr->fn_arg[1], scratch, strlen(tc_ptr->fn_arg[0])); - - /*** the TEST macro cannot be used here for some reason ****/ - - if (ret == -1) { - tst_resm(TFAIL, "readlink(2) failure on %s symbolic link file", - tc_ptr->fn_arg[1]); - - } else - if (strncmp(tc_ptr->fn_arg[0], scratch, strlen(tc_ptr->fn_arg[0])) - != 0) { - - /* Must null terminate scratch because readlink(2) doesn't */ - - scratch[strlen(tc_ptr->fn_arg[0])] = '\0'; - - tst_resm(TFAIL, - "readlink(2) Error : Expected %s symbolic link file contents but %s actual contents were returned", - tc_ptr->fn_arg[0], scratch); - } else { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } -} - -/*********************************************************************** - * This routine checks out the stat(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_stat(struct all_test_cases *tc_ptr) -{ - if (statter.st_dev != asymlink.st_dev) - tst_resm(TFAIL, - "stat of symbolic link reference to object device info %jd != stat of object file device info %jd", - (intmax_t) statter.st_dev, (intmax_t) asymlink.st_dev); - - else if (statter.st_mode != asymlink.st_mode) - tst_resm(TFAIL, - "stat of symbolic link reference to object file permissions %jd != stat of object file permissions %jd", - (intmax_t) statter.st_mode, - (intmax_t) asymlink.st_mode); - - else if (statter.st_nlink != asymlink.st_nlink) - tst_resm(TFAIL, - "stat of symbolic link reference to object file link count %jd != stat of object file link count %jd", - (intmax_t) statter.st_nlink, - (intmax_t) asymlink.st_nlink); - - else if (statter.st_uid != asymlink.st_uid) - tst_resm(TFAIL, - "stat of symbolic link reference to object file uid %jd != stat of object file uid %jd", - (intmax_t) statter.st_uid, (intmax_t) asymlink.st_uid); - - else if (statter.st_gid != asymlink.st_gid) - tst_resm(TFAIL, - "stat of symbolic link reference to object file gid %jd != stat of object file gid %jd", - (intmax_t) statter.st_gid, (intmax_t) asymlink.st_gid); - - else if (statter.st_size != asymlink.st_size) - tst_resm(TFAIL, - "stat of symbolic link reference to object file size %ld != stat of object file size %ld", - statter.st_size, asymlink.st_size); - - else if (statter.st_atime != asymlink.st_atime) - tst_resm(TFAIL, - "stat of symbolic link reference to object access time %ld != stat of object file access time %ld", - statter.st_atime, asymlink.st_atime); - - else if (statter.st_mtime != asymlink.st_mtime) - tst_resm(TFAIL, - "stat of symbolic link reference to object modify time %ld != stat of object file modify time %ld", - statter.st_atime, asymlink.st_atime); - - else if (statter.st_ctime != asymlink.st_ctime) - tst_resm(TFAIL, - "stat of symbolic link reference to object change time %ld != stat of object file change time %ld", - statter.st_atime, asymlink.st_atime); - else - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); -} - -/*********************************************************************** - * This routine checks out the chdir(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_chdir(struct all_test_cases *tc_ptr) -{ - if (mkdir(tc_ptr->fn_arg[2], MODE) == -1) - tst_resm(TFAIL, "Could not create a setup directory file"); - else { - - sprintf(Buf, "mkdir(%s, %#o) was successful\n", - tc_ptr->fn_arg[2], MODE); - strcat(Buffer, Buf); - - if (chdir(tc_ptr->fn_arg[1]) == -1) - tst_resm(TFAIL, - "%sCould not change a directory file through a %s", - Buffer, - "symbolic link which which pointed at object"); - else { - - char *cwd; - char expected_location[PATH_MAX]; - /* - * Build expected current directory position - */ - /* FIXME (garrcoop): memory leak */ - strcpy(expected_location, tst_get_tmpdir()); - strcat(expected_location, "/"); - strcat(expected_location, tc_ptr->fn_arg[2]); - - if ((cwd = getcwd(NULL, 0)) == NULL) { - tst_resm(TFAIL, "getcwd(3) FAILURE"); - } else if (strcmp(cwd, expected_location) == 0) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, - "%s%s %s %s not equal to expected %s", - Buffer, - "chdir(2) returned successfully, but getcwd(3) indicated", - "new current working directory location", - cwd, expected_location); - } - /* FIXME (garrcoop): memory leak */ - chdir(tst_get_tmpdir()); - } - rmdir(tc_ptr->fn_arg[2]); - } -} - -/*********************************************************************** - * This routine checks out the link(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_link(struct all_test_cases *tc_ptr) -{ - struct stat stbuf; - - if (link(tc_ptr->fn_arg[1], "nick") == -1) { - tst_resm(TFAIL, "%slink(%s, \"nick\") failed, errno: %d: %s %s", - Buffer, tc_ptr->fn_arg[1], errno, - "link of new file to object file via symbolic link file failed", - "when expected not to"); - } else { - sprintf(Buf, "link(%s, \"nick\") was successful\n", - tc_ptr->fn_arg[1]); - strcat(Buffer, Buf); - - /* - * Check that links counts were properly set - */ - if (lstat(tc_ptr->fn_arg[1], &asymlink) == -1) { - tst_resm(TBROK, "lstat(%s) failed. errno: %d", - tc_ptr->fn_arg[1], errno); - - } else if (lstat("nick", &statter) == -1) { - tst_resm(TBROK, "lstat(nick) failed, errno:%d", - errno); - } else { - if (statter.st_ino == asymlink.st_ino) { - if ((statter.st_nlink == 2) && (asymlink.st_nlink == 2)) { - tst_resm(TEST_RESULT, "%s", - msgs[tc_ptr->pass_msg]); - } else { - lstat(tc_ptr->fn_arg[2], - &stbuf); - - tst_resm(TFAIL, - "%slink(%s, %s) failed to adjust link count.\n\ - count for nick is %d, count for %s is %d, count for %s is %d.", - Buffer, tc_ptr->fn_arg[1], "nick", statter.st_nlink, tc_ptr->fn_arg[1], asymlink.st_nlink, tc_ptr->fn_arg[2], - stbuf.st_nlink); - } - } else { - tst_resm(TFAIL, "%sA lstat of %s (ino:%jd) and of\n\t\t\ -%s (ino:%jd), does not show them being the same ino.", Buffer, - tc_ptr->fn_arg[1], (intmax_t) asymlink.st_ino, "nick", (intmax_t) statter.st_ino); - } - } - - delete_files("nick", NULL); - } -} - -/*********************************************************************** - * This routine checks out the unlink(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_unlink(struct all_test_cases *tc_ptr) -{ - if (stat(tc_ptr->fn_arg[2], &asymlink) == -1) - tst_resm(TBROK, "stat(2) Failure when accessing %s object file", - tc_ptr->fn_arg[2]); - else if (unlink(tc_ptr->fn_arg[1]) == -1) - tst_resm(TFAIL, - "unlink(2) failed when removing symbolic link file"); - else { - sprintf(Buf, "unlink(%s) was successful\n", tc_ptr->fn_arg[1]); - strcat(Buffer, Buf); - if (stat(tc_ptr->fn_arg[2], &statter) == -1) { - tst_resm(TFAIL, "%s %s", - "unlink(2) failed because it not only removed symbolic link", - "file which pointed at object file, but object file as well"); - - } else if ((statter.st_ino == asymlink.st_ino) && - (statter.st_dev == asymlink.st_dev) && - (statter.st_size == asymlink.st_size)) { - - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s%s %s %s", Buffer, - "unlink(2) failed because it not only removed symbolic link", - "file which pointed at object file, but it changed object", - "file inode information"); - } - } - -} - -/*********************************************************************** - * This routine checks out the chmod(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_chmod(struct all_test_cases *tc_ptr) -{ - if (stat(tc_ptr->fn_arg[2], &asymlink) == -1) - tst_resm(TBROK, "stat(2) Failure when accessing %s object file", - tc_ptr->fn_arg[2]); - else if (chmod(tc_ptr->fn_arg[1], (MODE | MASK)) == -1) - tst_resm(TFAIL, "%s%s %s", Buffer, - "chmod(2) failed when changing file permission", - "through symbolic link file"); - else { - sprintf(Buf, "chmod(%s, %#o) was successful\n", - tc_ptr->fn_arg[1], (MODE | MASK)); - strcat(Buffer, Buf); - - if (stat(tc_ptr->fn_arg[2], &statter) == -1) { - tst_resm(TBROK, - "stat(2) Failure when accessing %s object file", - tc_ptr->fn_arg[2]); - } else if ((statter.st_mode == (MODE | MASK)) - && (statter.st_mode != asymlink.st_mode)) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s%s %o to %o %s", Buffer, - "chmod(2) failed to change object file permissions from", - asymlink.st_mode, (MODE | MASK), - "through symbolic link file"); - } - } - -} - -/*********************************************************************** - * This routine checks out the utime(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_utime(struct all_test_cases *tc_ptr) -{ - struct utimbuf utimes; - - if (stat(tc_ptr->fn_arg[2], &asymlink) == -1) - tst_resm(TBROK, "stat(2) Failure when accessing %s object file", - tc_ptr->fn_arg[2]); - else { - /* Now add a few values to access and modify times */ - - utimes.actime = asymlink.st_atime + a_time_value; - utimes.modtime = asymlink.st_mtime + a_time_value; - - /* Now hand off to utime(2) via symbolic link file */ - - if (utime(tc_ptr->fn_arg[1], &utimes) == -1) - tst_resm(TFAIL, "%s %s", - "utime(2) failed to process object file access and modify", - "time updates through symbolic link"); - else { - /* Now verify changes were made */ - - if (stat(tc_ptr->fn_arg[2], &statter) == -1) - tst_resm(TBROK, - "stat(2) Failure when accessing %s object file", - tc_ptr->fn_arg[2]); - else { - time_t temp, diff; - - temp = statter.st_atime - asymlink.st_atime; - diff = - (statter.st_mtime - asymlink.st_mtime) - - temp; - - if (!diff) { - tst_resm(TEST_RESULT, "%s", - msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, - "%s %s %jd greater than original times", - "utime(2) failed to change object file access and", - "modify times through symbolic link to a value", - (intmax_t) a_time_value); - } - } - } - } -} - -/*********************************************************************** - * This routine checks out the rename(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_rename(struct all_test_cases *tc_ptr) -{ - int pts_at_object = 0; - - if (stat(tc_ptr->fn_arg[2], &statter) != -1) - pts_at_object = 1; - - TEST(rename(tc_ptr->fn_arg[1], A_S_FILE)); - errno = TEST_ERRNO; - if (TEST_RETURN == -1) { - tst_resm(TFAIL, - "rename(3) failed to rename %s symbolic link file to %s", - tc_ptr->fn_arg[2], A_S_FILE); - } else if (pts_at_object) { - if (ck_both(tc_ptr->fn_arg[0], A_S_FILE, tc_ptr->fn_arg[2])) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s", test_msg); - } - } else if (!ck_symlink(tc_ptr->fn_arg[0], A_S_FILE, NULL)) { - tst_resm(TFAIL, "%s", test_msg); - } else if (stat(tc_ptr->fn_arg[1], &asymlink) != -1) { - tst_resm(TFAIL, - "rename(3) did not remove %s when renaming to %s", - tc_ptr->fn_arg[1], A_S_FILE); - } else { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } -} - -/*********************************************************************** - * This routine checks out the open(2) system call for a successful - * invocation - * - * Argument is pointer to test_objects array of structures of type - * all_test_cases - ***********************************************************************/ -void do_open(struct all_test_cases *tc_ptr) -{ - int fd = -1; - int ret, pts_at_object = 0; - char scratch[PATH_MAX]; - - if (stat(tc_ptr->fn_arg[2], &statter) != -1) - pts_at_object = 1; - - if (pts_at_object) { - TEST(open(tc_ptr->fn_arg[1], O_RDWR)); - errno = TEST_ERRNO; - if ((fd = TEST_RETURN) == -1) { - tst_resm(TFAIL, - "open(2) Failure when opening object file through symbolic link file"); - return; - } - } else { - TEST(open(tc_ptr->fn_arg[1], (O_CREAT | O_RDWR), MODE)); - errno = TEST_ERRNO; - if ((fd = TEST_RETURN) == -1) { - tst_resm(TFAIL, - "open(2) Failure when creating object file through symbolic link file"); - return; - } - } - if ((ret = write(fd, BIG_STRING, strlen(BIG_STRING))) == -1) { - tst_resm(TFAIL, - "write(2) Failure to object file opened through a symbolic link file: errno:%d", - errno); - } else if (ret != strlen(BIG_STRING)) { - tst_resm(TFAIL, - "write(2) Failed to write %zu bytes to object file opened through a symbolic link file", - strlen(BIG_STRING)); - } else if (lseek(fd, 0L, 0) == -1) { - tst_resm(TFAIL, - "lseek(2) Failed to position to beginning of object file opened through a symbolic link file: errno = %d", - errno); - } else if ((ret = read(fd, scratch, strlen(BIG_STRING))) == -1) { - tst_resm(TFAIL, - "read(2) Failure of object file opened through a symbolic link file: errno = %d", - errno); - } else if (ret != strlen(BIG_STRING)) { - tst_resm(TFAIL, - "read(2) Failed to read %zu bytes to object file opened through a symbolic link file", - strlen(BIG_STRING)); - } else if (strncmp(BIG_STRING, scratch, strlen(BIG_STRING)) != 0) { - tst_resm(TFAIL, - "Content of write(2) and read(2) Failed to object file through symbolic link file was not as expected. Expected %s and read returned %s", - BIG_STRING, scratch); - } else { - /* - * Close off symbolic link file to object file access - */ - if (close(fd) == -1) { - tst_resm(TFAIL, - "close(2) Failure when closing object file accessed symbolic link file"); - } - /* - * Now, lets open up and read object file and compare contents - */ - else if ((fd = open(tc_ptr->fn_arg[0], O_RDONLY)) == -1) { - tst_resm(TFAIL, - "open(2) Failure when opening %s file: errno:%d %s", - tc_ptr->fn_arg[0], errno, strerror(errno)); - } else if ((ret = read(fd, scratch, strlen(BIG_STRING))) == -1) { - tst_resm(TFAIL, - "read(2) Failure of object file opened through a symbolic link file: errno:%d", - errno); - } else if (ret != strlen(BIG_STRING)) { - tst_resm(TFAIL, - "read(2) Failed to read %zu bytes to object file opened through a symbolic link file", - strlen(BIG_STRING)); - } else if (strncmp(BIG_STRING, scratch, strlen(BIG_STRING)) != - 0) { - tst_resm(TFAIL, - "Content of write(2) and read(2) Failed to object file through symbolic link file was not as expected. Expected %s and read returned %s", - BIG_STRING, scratch); - } else if (pts_at_object) { - tst_resm(TEST_RESULT, "%s", msgs[tc_ptr->pass_msg]); - } else { /* Insure newly created object file is pointed at */ - if (ck_both - (tc_ptr->fn_arg[0], tc_ptr->fn_arg[1], - tc_ptr->fn_arg[0])) { - tst_resm(TEST_RESULT, "%s", - msgs[tc_ptr->pass_msg]); - } else { - tst_resm(TFAIL, "%s", test_msg); - } - } - close(fd); - } -} - -/*************************************************************** - * setup() - performs all ONE TIME setup for this test. - ***************************************************************/ -void setup(void) -{ - - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - /* create a temporary directory and go to it */ - tst_tmpdir(); - -} - -/*************************************************************** - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - ***************************************************************/ -void cleanup(void) -{ - - tst_rmdir(); - -} - -void help(void) -{ - unsigned int ind; - - printf(" -T id Determines which tests cases to execute:\n"); - - for (ind = 0; ind < sizeof(all_tcses) / sizeof(struct tcses); ind++) { - printf(" %s/%s - %s\n", all_tcses[ind].tcid, - all_tcses[ind].syscall, all_tcses[ind].desc); - } -} diff --git a/testcases/kernel/syscalls/symlink/symlink02.c b/testcases/kernel/syscalls/symlink/symlink02.c index c18db2b3..e5cf1c8a 100755 --- a/testcases/kernel/syscalls/symlink/symlink02.c +++ b/testcases/kernel/syscalls/symlink/symlink02.c @@ -1,208 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * 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/ - * + * Copyright (c) Linux Test Project, 2001-2023 + * Author: William Roske */ -/* $Id: symlink02.c,v 1.6 2009/08/28 14:17:14 vapier Exp $ */ -/********************************************************** - * - * OS Test - Silicon Graphics, Inc. - * - * TEST IDENTIFIER : symlink02 - * - * EXECUTED BY : anyone - * - * TEST TITLE : Basic test for symlink(2) - * - * PARENT DOCUMENT : usctpl01 - * - * TEST CASE TOTAL : 1 - * - * WALL CLOCK TIME : 1 - * - * CPU TYPES : ALL - * - * AUTHOR : William Roske - * - * CO-PILOT : Dave Fenner - * - * DATE STARTED : 03/30/92 - * - * INITIAL RELEASE : UNICOS 7.0 - * - * TEST CASES - * - * 1.) symlink(2) returns...(See Description) - * - * INPUT SPECIFICATIONS - * The standard options for system call tests are accepted. - * (See the parse_opts(3) man page). - * - * OUTPUT SPECIFICATIONS - *$ - * DURATION - * Terminates - with frequency and infinite modes. - * - * SIGNALS - * Uses SIGUSR1 to pause before test if option set. - * (See the parse_opts(3) man page). - * - * RESOURCES - * None - * - * ENVIRONMENTAL NEEDS - * No run-time environmental needs. - * - * SPECIAL PROCEDURAL REQUIREMENTS - * None - * - * INTERCASE DEPENDENCIES - * None - * - * DETAILED DESCRIPTION - * This is a Phase I test for the symlink(2) system call. It is intended - * to provide a limited exposure of the system call, for now. It - * should/will be extended when full functional tests are written for - * symlink(2). - * - * Setup: - * Setup signal handling. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, Issue a PASS message. - * - * Cleanup: - * Print errno log and/or timing stats if options given - * - * - *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/ -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +/*\ + * Check the basic functionality of the symlink() system call. + */ -void setup(); -void cleanup(); +#include "tst_test.h" -char *TCID = "symlink02"; -int TST_TOTAL = 1; +static char *fname, *symlnk; -char fname[255], symlnk[255]; -int fd; - -int main(int ac, char **av) +static void verify_symlink(void) { - int lc; - - /*************************************************************** - * parse standard options - ***************************************************************/ - tst_parse_opts(ac, av, NULL, NULL); - - /*************************************************************** - * perform global setup for test - ***************************************************************/ - setup(); - - /*************************************************************** - * check looping state if -c option given - ***************************************************************/ - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Call symlink(2) - */ - TEST(symlink(fname, symlnk)); - - /* check return code */ - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "symlink(%s, %s) Failed, errno=%d : %s", - fname, symlnk, TEST_ERRNO, - strerror(TEST_ERRNO)); - } else { - SAFE_UNLINK(cleanup, symlnk); - } - } - - /*************************************************************** - * cleanup and exit - ***************************************************************/ - cleanup(); - tst_exit(); + TST_EXP_POSITIVE(symlink(fname, symlnk), "symlink(%s, %s)", + fname, symlnk); + if (TST_RET == -1) + tst_res(TFAIL, "symlink(%s, %s) Failed", fname, symlnk); + else + SAFE_UNLINK(symlnk); } -/*************************************************************** - * setup() - performs all ONE TIME setup for this test. - ***************************************************************/ -void setup(void) +static void setup(void) { + fname = tst_aprintf("tfile_%d", getpid()); - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); - - sprintf(fname, "tfile_%d", getpid()); - if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1) { - tst_brkm(TBROK, cleanup, - "open(%s, O_RDWR|O_CREAT,0700) Failed, errno=%d : %s", - fname, errno, strerror(errno)); - } - - if (close(fd) == -1) { - tst_resm(TWARN, "close(%s) Failed, errno=%d : %s", - fname, errno, strerror(errno)); - } - sprintf(symlnk, "st_%d", getpid()); + symlnk = tst_aprintf("st_%d", getpid()); } -/*************************************************************** - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - ***************************************************************/ -void cleanup(void) -{ - - tst_rmdir(); - -} +static struct tst_test test = { + .needs_tmpdir = 1, + .setup = setup, + .test_all = verify_symlink, +}; diff --git a/testcases/kernel/syscalls/symlink/symlink04.c b/testcases/kernel/syscalls/symlink/symlink04.c index 2190b3b1..b8aca241 100755 --- a/testcases/kernel/syscalls/symlink/symlink04.c +++ b/testcases/kernel/syscalls/symlink/symlink04.c @@ -1,193 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) Linux Test Project, 2001-2023 + * Author: John George */ -/* - * Test Name : symlink04 - * - * Test Description : - * Verify that, symlink will succeed to creat a symbolic link of an existing - * object name path. - * - * Expected Result: - * symlink() should return value 0 on success and symbolic link of an - * existing object should be created. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * symlink04 [-c n] [-e] [-f] [-i n] [-I x] [-p x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 07/2001 John George - * -Ported - * - * Restrictions: - * None. - * +/*\ + * Check that a symbolic link may point to an existing file or + * to a nonexistent one. */ +#include #include -#include -#include -#include -#include -#include -#include -#include "test.h" -#include "safe_macros.h" +#include "tst_test.h" -#define TESTFILE "testfile" -#define SYMFILE "slink_file" -#define FILE_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH +#define TESTFILE "testfile" +#define NONFILE "noexistfile" +#define SYMFILE "slink_file" -char *TCID = "symlink04"; -int TST_TOTAL = 1; +static char *testfile; +static char *nonfile; -void setup(); -void cleanup(); +static struct tcase { + char **srcfile; +} tcases[] = { + {&testfile}, + {&nonfile}, +}; -int main(int ac, char **av) +static void setup(void) { - struct stat stat_buf; /* stat structure buffer */ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Call symlink(2) to create a symlink of - * testfile. - */ - TEST(symlink(TESTFILE, SYMFILE)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL, "symlink(%s, %s) Failed, errno=%d : %s", - TESTFILE, SYMFILE, TEST_ERRNO, - strerror(TEST_ERRNO)); - } else { - /* - * Get the symlink file status information - * using lstat(2). - */ - if (lstat(SYMFILE, &stat_buf) < 0) { - tst_brkm(TFAIL, cleanup, "lstat(2) of " - "%s failed, error:%d", SYMFILE, - errno); - } - - /* Check if the st_mode contains a link */ - if (!S_ISLNK(stat_buf.st_mode)) { - tst_resm(TFAIL, - "symlink of %s doesn't exist", - TESTFILE); - } else { - tst_resm(TPASS, "symlink(%s, %s) " - "functionality successful", - TESTFILE, SYMFILE); - } - } - - /* Unlink the symlink file for next loop */ - SAFE_UNLINK(cleanup, SYMFILE); - tst_count++; /* incr TEST_LOOP counter */ - } - - cleanup(); - tst_exit(); - + SAFE_TOUCH(TESTFILE, 0644, NULL); } -/* - * void - * setup() - performs all ONE TIME setup for this test. - * Create a temporary directory and change directory to it. - * Create a test file under temporary directory and close it - */ -void setup(void) +static void verify_symlink(unsigned int i) { - int fd; /* file handle for testfile */ + struct tcase *tc = &tcases[i]; - tst_sig(NOFORK, DEF_HANDLER, cleanup); + struct stat stat_buf; - /* Pause if that option was specified - * TEST_PAUSE contains the code to fork the test with the -i option. - * You want to make sure you do this before you create your temporary - * directory. - */ - TEST_PAUSE; + TST_EXP_PASS(symlink(*tc->srcfile, SYMFILE)); - tst_tmpdir(); + SAFE_LSTAT(SYMFILE, &stat_buf); - /* creat/open a testfile */ - if ((fd = open(TESTFILE, O_RDWR | O_CREAT, FILE_MODE)) == -1) { - tst_brkm(TBROK, cleanup, - "open(%s, O_RDWR|O_CREAT, %#o) Failed, errno=%d : %s", - TESTFILE, FILE_MODE, errno, strerror(errno)); - } + if (!S_ISLNK(stat_buf.st_mode)) + tst_res(TFAIL, "symlink of %s doesn't exist", *tc->srcfile); - /* Close the temporary file created above */ - if (close(fd) == -1) { - tst_resm(TBROK, "close(%s) Failed, errno=%d : %s", - TESTFILE, errno, strerror(errno)); - } + SAFE_UNLINK(SYMFILE); } -/* - * void - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Remove the test directory and testfile created in the setup. - */ -void cleanup(void) -{ - - tst_rmdir(); - -} +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .setup = setup, + .test = verify_symlink, + .bufs = (struct tst_buffers []) { + {&testfile, .str = TESTFILE}, + {&nonfile, .str = NONFILE}, + {}, + }, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/symlink/symlink05.c b/testcases/kernel/syscalls/symlink/symlink05.c deleted file mode 100755 index 83b151f5..00000000 --- a/testcases/kernel/syscalls/symlink/symlink05.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Test Name : symlink05 - * - * Test Description : - * Verify that, symlink will succeed to creat a symbolic link of an - * non-existing object name path. - * - * Expected Result: - * symlink() should return value 0 on success and symlink of an - * non-existing object should be created. - * - * Algorithm: - * Setup: - * Setup signal handling. - * Create temporary directory. - * Pause for SIGUSR1 if option specified. - * - * Test: - * Loop if the proper options are given. - * Execute system call - * Check return code, if system call failed (return=-1) - * Log the errno and Issue a FAIL message. - * Otherwise, - * Verify the Functionality of system call - * if successful, - * Issue Functionality-Pass message. - * Otherwise, - * Issue Functionality-Fail message. - * Cleanup: - * Print errno log and/or timing stats if options given - * Delete the temporary directory created. - * - * Usage: - * symlink05 [-c n] [-e] [-f] [-i n] [-I x] [-p x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -f : Turn off functionality Testing. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 07/2001 John George - * -Ported - * - * Restrictions: - * This test should be run by 'non-super-user' only. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "test.h" -#include "safe_macros.h" - -#define TESTFILE "testfile" -#define SYMFILE "slink_file" - -char *TCID = "symlink05"; -int TST_TOTAL = 1; - -void setup(); -void cleanup(); - -int main(int ac, char **av) -{ - struct stat stat_buf; /* stat structure buffer */ - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - - tst_count = 0; - - /* - * Call symlink(2) to create a symlink of - * an non-existing testfile. - */ - TEST(symlink(TESTFILE, SYMFILE)); - - if (TEST_RETURN == -1) { - tst_resm(TFAIL, - "symlink(%s, %s) Failed, errno=%d : %s", - TESTFILE, SYMFILE, TEST_ERRNO, - strerror(TEST_ERRNO)); - } else { - /* - * Get the symlink file status information - * using lstat(2). - */ - if (lstat(SYMFILE, &stat_buf) < 0) { - tst_brkm(TFAIL, cleanup, "lstat(2) of " - "%s failed, error:%d", - SYMFILE, errno); - } - - /* Check if the st_mode contains a link */ - if (!S_ISLNK(stat_buf.st_mode)) { - tst_resm(TFAIL, - "symlink of %s doesn't exist", - TESTFILE); - } else { - tst_resm(TPASS, "symlink(%s, %s) " - "functionality successful", - TESTFILE, SYMFILE); - } - } - - /* Unlink the symlink file for next loop */ - SAFE_UNLINK(cleanup, SYMFILE); - tst_count++; /* incr TEST_LOOP counter */ - } - - cleanup(); - tst_exit(); - -} - -/* - * void - * setup() - performs all ONE TIME setup for this test. - * Create a temporary directory and change directory to it. - */ -void setup(void) -{ - - tst_sig(NOFORK, DEF_HANDLER, cleanup); - - /* Pause if that option was specified - * TEST_PAUSE contains the code to fork the test with the -i option. - * You want to make sure you do this before you create your temporary - * directory. - */ - TEST_PAUSE; - - tst_tmpdir(); - -} - -/* - * void - * cleanup() - performs all ONE TIME cleanup for this test at - * completion or premature exit. - * Remove the temporary directory created in the setup. - */ -void cleanup(void) -{ - - tst_rmdir(); - -} diff --git a/testcases/kernel/syscalls/sync_file_range/sync_file_range01.c b/testcases/kernel/syscalls/sync_file_range/sync_file_range01.c index 47188aa4..ead4314a 100755 --- a/testcases/kernel/syscalls/sync_file_range/sync_file_range01.c +++ b/testcases/kernel/syscalls/sync_file_range/sync_file_range01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic error conditions test for sync_file_range() system call, tests for: * * - EBADFD Wrong filedescriptor diff --git a/testcases/kernel/syscalls/sync_file_range/sync_file_range02.c b/testcases/kernel/syscalls/sync_file_range/sync_file_range02.c index 28a8156c..761a396f 100755 --- a/testcases/kernel/syscalls/sync_file_range/sync_file_range02.c +++ b/testcases/kernel/syscalls/sync_file_range/sync_file_range02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tests if sync_file_range() does sync a test file range with a many dirty pages * to a block device. Also, it tests all supported filesystems on a test block * device. diff --git a/testcases/kernel/syscalls/syscall/syscall01.c b/testcases/kernel/syscalls/syscall/syscall01.c index 76e79322..deb8a4e5 100755 --- a/testcases/kernel/syscalls/syscall/syscall01.c +++ b/testcases/kernel/syscalls/syscall/syscall01.c @@ -3,10 +3,13 @@ * Copyright (c) International Business Machines Corp., 2002 * 01/02/2003 Port to LTP avenkat@us.ibm.com * 06/30/2001 Port to Linux nsharoff@us.ibm.com + * Copyright (c) Linux Test Project, 2002-2024 */ -/* +/*\ * Basic test for syscall(). + * + * Compare raw get{g,p,u}id results with their glibc wrappers. */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/sysfs/sysfs01.c b/testcases/kernel/syscalls/sysfs/sysfs01.c index 3a91fcb0..a83053df 100755 --- a/testcases/kernel/syscalls/sysfs/sysfs01.c +++ b/testcases/kernel/syscalls/sysfs/sysfs01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test is run for option 1 for sysfs(2). * Translate the filesystem identifier string fsname into a filesystem type index. */ diff --git a/testcases/kernel/syscalls/sysfs/sysfs02.c b/testcases/kernel/syscalls/sysfs/sysfs02.c index b6d81036..4bca5161 100755 --- a/testcases/kernel/syscalls/sysfs/sysfs02.c +++ b/testcases/kernel/syscalls/sysfs/sysfs02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test is run for option 2 for sysfs(2). * Translate the filesystem type index fs_index into a null-terminated filesystem * identifier string. This string will be written to the buffer pointed to by buf. diff --git a/testcases/kernel/syscalls/sysfs/sysfs03.c b/testcases/kernel/syscalls/sysfs/sysfs03.c index b679d780..aae962f8 100755 --- a/testcases/kernel/syscalls/sysfs/sysfs03.c +++ b/testcases/kernel/syscalls/sysfs/sysfs03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test is run for option 3 for sysfs(2). * Return the total number of filesystem types currently present in the kernel. */ diff --git a/testcases/kernel/syscalls/sysfs/sysfs04.c b/testcases/kernel/syscalls/sysfs/sysfs04.c index ffda51bf..ef504b93 100755 --- a/testcases/kernel/syscalls/sysfs/sysfs04.c +++ b/testcases/kernel/syscalls/sysfs/sysfs04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test case checks whether sysfs(2) system call returns * appropriate error number for invalid option. */ diff --git a/testcases/kernel/syscalls/sysfs/sysfs05.c b/testcases/kernel/syscalls/sysfs/sysfs05.c index bfcead7d..922c4630 100755 --- a/testcases/kernel/syscalls/sysfs/sysfs05.c +++ b/testcases/kernel/syscalls/sysfs/sysfs05.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * This test case checks whether sysfs(2) system call returns appropriate * error number for invalid option and for invalid filesystem name and fs index out of bounds. */ diff --git a/testcases/kernel/syscalls/sysinfo/sysinfo02.c b/testcases/kernel/syscalls/sysinfo/sysinfo02.c index 7ad0e8bd..4ce06e0a 100755 --- a/testcases/kernel/syscalls/sysinfo/sysinfo02.c +++ b/testcases/kernel/syscalls/sysinfo/sysinfo02.c @@ -78,8 +78,6 @@ void cleanup(); char *TCID = "sysinfo02"; int TST_TOTAL = 1; -#if !defined(UCLINUX) - int main(int ac, char **av) { struct sysinfo *sysinfo_buf; @@ -115,16 +113,6 @@ int main(int ac, char **av) } -#else - -int main(void) -{ - tst_resm(TINFO, "test is not available on uClinux"); - tst_exit(); -} - -#endif /* if !defined(UCLINUX) */ - /* * setup() * performs one time setup diff --git a/testcases/kernel/syscalls/syslog/syslog11.c b/testcases/kernel/syscalls/syslog/syslog11.c index 733da5f9..28a6fdf4 100755 --- a/testcases/kernel/syscalls/syslog/syslog11.c +++ b/testcases/kernel/syscalls/syslog/syslog11.c @@ -5,8 +5,6 @@ */ /* - * [Description] - * * Verify that, syslog(2) is successful for type ranging from 1 to 8 */ diff --git a/testcases/kernel/syscalls/syslog/syslog12.c b/testcases/kernel/syscalls/syslog/syslog12.c index e8b39a11..9d519b54 100755 --- a/testcases/kernel/syscalls/syslog/syslog12.c +++ b/testcases/kernel/syscalls/syslog/syslog12.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that syslog(2) system call fails with appropriate error number: * * 1. EINVAL -- invalid type/command diff --git a/testcases/kernel/syscalls/tee/tee02.c b/testcases/kernel/syscalls/tee/tee02.c index 5ebb3c3f..0ee94000 100755 --- a/testcases/kernel/syscalls/tee/tee02.c +++ b/testcases/kernel/syscalls/tee/tee02.c @@ -3,15 +3,13 @@ * Copyright (c) 2014 Fujitsu Ltd. * Author: Xing Gu */ -/* - * Description: - * Verify that, - * 1) tee() returns -1 and sets errno to EINVAL if fd_in does - * not refer to a pipe. - * 2) tee() returns -1 and sets errno to EINVAL if fd_out does - * not refer to a pipe. - * 3) tee() returns -1 and sets errno to EINVAL if fd_in and - * fd_out refer to the same pipe. + +/*\ + * Verify that, tee(2) returns -1 and sets errno to: + * + * 1. EINVAL if fd_in does not refer to a pipe. + * 2. EINVAL if fd_out does not refer to a pipe. + * 3. EINVAL if fd_in and fd_out refer to the same pipe. */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/time/time01.c b/testcases/kernel/syscalls/time/time01.c index d8625c04..9b176f89 100755 --- a/testcases/kernel/syscalls/time/time01.c +++ b/testcases/kernel/syscalls/time/time01.c @@ -4,10 +4,9 @@ */ /*\ - * [Description] - * - Basic test for the time(2) system call. It is intended to provide a - * limited exposure of the system call. - * - Verify that time(2) returns the value of time in seconds since the Epoch + * Basic test for the time(2) system call. + * + * Verify that time(2) returns the value of time in seconds since the Epoch * and stores this value in the memory pointed to by the parameter. */ @@ -16,8 +15,8 @@ #include "tst_test.h" -time_t tlocal; -time_t *targs[] = { +static time_t tlocal; +static time_t *targs[] = { NULL, &tlocal, }; @@ -32,16 +31,17 @@ static void verify_time(unsigned int i) return; } - if (!tloc) + if (!tloc) { tst_res(TPASS, "time() returned value %ld", TST_RET); - else if (*tloc == TST_RET) + } else if (*tloc == TST_RET) { tst_res(TPASS, "time() returned value %ld, stored value %jd are same", TST_RET, (intmax_t) *tloc); - else + } else { tst_res(TFAIL, "time() returned value %ld, stored value %jd are different", TST_RET, (intmax_t) *tloc); + } } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/timer_create/timer_create01.c b/testcases/kernel/syscalls/timer_create/timer_create01.c index d56e68c2..dfbdc912 100755 --- a/testcases/kernel/syscalls/timer_create/timer_create01.c +++ b/testcases/kernel/syscalls/timer_create/timer_create01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2003. All Rights Reserved. * diff --git a/testcases/kernel/syscalls/timer_create/timer_create02.c b/testcases/kernel/syscalls/timer_create/timer_create02.c index 1920f087..eda8d547 100755 --- a/testcases/kernel/syscalls/timer_create/timer_create02.c +++ b/testcases/kernel/syscalls/timer_create/timer_create02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2003. All Rights Reserved. * @@ -25,6 +25,7 @@ #include #include "tst_test.h" #include "lapi/common_timers.h" +#include "tst_safe_clocks.h" static struct sigevent sig_ev = { .sigev_notify = SIGEV_NONE, @@ -42,30 +43,32 @@ static struct sigevent sig_ev_inv_sig = { }; static kernel_timer_t timer_id; +static clock_t max_clocks; +static clock_t clk_realtime = CLOCK_REALTIME; static struct testcase { - clock_t clock; + clock_t *clock; struct sigevent *ev_ptr; kernel_timer_t *kt_ptr; int error; char *desc; } tcases[] = { - {CLOCK_REALTIME, NULL, &timer_id, EFAULT, "invalid sigevent struct"}, - {CLOCK_REALTIME, &sig_ev, NULL, EFAULT, "invalid timer ID"}, - {MAX_CLOCKS, &sig_ev, &timer_id, EINVAL, "invalid clock"}, - {CLOCK_REALTIME, &sig_ev_inv_not, &timer_id, EINVAL, "wrong sigev_notify"}, - {CLOCK_REALTIME, &sig_ev_inv_sig, &timer_id, EINVAL, "wrong sigev_signo"}, + {&clk_realtime, NULL, &timer_id, EFAULT, "invalid sigevent struct"}, + {&clk_realtime, &sig_ev, NULL, EFAULT, "invalid timer ID"}, + {&max_clocks, &sig_ev, &timer_id, EINVAL, "invalid clock"}, + {&clk_realtime, &sig_ev_inv_not, &timer_id, EINVAL, "wrong sigev_notify"}, + {&clk_realtime, &sig_ev_inv_sig, &timer_id, EINVAL, "wrong sigev_signo"}, }; static void run(unsigned int n) { struct testcase *tc = &tcases[n]; - TEST(tst_syscall(__NR_timer_create, tc->clock, tc->ev_ptr, tc->kt_ptr)); + TEST(tst_syscall(__NR_timer_create, *tc->clock, tc->ev_ptr, tc->kt_ptr)); if (TST_RET != -1 || TST_ERR != tc->error) { tst_res(TFAIL | TTERRNO, - "%s idn't fail as expected (%s) - Got", + "%s did not fail as expected (%s) - Got", tc->desc, tst_strerrno(tc->error)); return; } @@ -84,6 +87,8 @@ static void setup(void) if (!tcases[i].kt_ptr) tcases[i].kt_ptr = tst_get_bad_addr(NULL); } + + max_clocks = tst_get_max_clocks(); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/timer_delete/timer_delete01.c b/testcases/kernel/syscalls/timer_delete/timer_delete01.c index bdc2e44c..4d1b6ddf 100755 --- a/testcases/kernel/syscalls/timer_delete/timer_delete01.c +++ b/testcases/kernel/syscalls/timer_delete/timer_delete01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2003. All Rights Reserved. * diff --git a/testcases/kernel/syscalls/timer_delete/timer_delete02.c b/testcases/kernel/syscalls/timer_delete/timer_delete02.c index 29614f62..9df959f9 100755 --- a/testcases/kernel/syscalls/timer_delete/timer_delete02.c +++ b/testcases/kernel/syscalls/timer_delete/timer_delete02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2003. All Rights Reserved. * diff --git a/testcases/kernel/syscalls/timer_getoverrun/timer_getoverrun01.c b/testcases/kernel/syscalls/timer_getoverrun/timer_getoverrun01.c index aa9881f2..a015cbe4 100755 --- a/testcases/kernel/syscalls/timer_getoverrun/timer_getoverrun01.c +++ b/testcases/kernel/syscalls/timer_getoverrun/timer_getoverrun01.c @@ -1,88 +1,44 @@ -/****************************************************************************** - * Copyright (c) Crackerjack Project., 2007 * - * Porting from Crackerjack to LTP is done by: * - * Manas Kumar Nayak * - * Copyright (c) 2013 Cyril Hrubis * - * * - * 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 * - * * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2001 + * Porting from Crackerjack to LTP is done by: + * Manas Kumar Nayak + * + * Copyright (c) Linux Test Project, 2009-2023 + * Copyright (c) 2013 Cyril Hrubis + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ + +/*\ + * This test checks base timer_getoverrun() functionality. + */ -#include -#include -#include #include -#include - -#include "test.h" +#include +#include "tst_safe_clocks.h" #include "lapi/syscalls.h" +#include "lapi/common_timers.h" -char *TCID = "timer_getoverrun01"; -int TST_TOTAL = 1; - -static void cleanup(void) +static void run(void) { - - tst_rmdir(); -} - -static void setup(void) -{ - TEST_PAUSE; - tst_tmpdir(); -} - -int main(int ac, char **av) -{ - int lc; - int timer; + kernel_timer_t timer; struct sigevent ev; - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - ev.sigev_value = (union sigval) 0; - ev.sigev_signo = SIGALRM; ev.sigev_notify = SIGEV_SIGNAL; - TEST(tst_syscall(__NR_timer_create, CLOCK_REALTIME, &ev, &timer)); + ev.sigev_signo = SIGALRM; - if (TEST_RETURN != 0) - tst_brkm(TBROK | TTERRNO, cleanup, "Failed to create timer"); + if (tst_syscall(__NR_timer_create, CLOCK_REALTIME, &ev, &timer)) + tst_brk(TBROK | TERRNO, "timer_create() failed"); - for (lc = 0; TEST_LOOPING(lc); ++lc) { - tst_count = 0; + TST_EXP_POSITIVE(tst_syscall(__NR_timer_getoverrun, timer)); - TEST(tst_syscall(__NR_timer_getoverrun, timer)); - if (TEST_RETURN == 0) { - tst_resm(TPASS, - "timer_getoverrun(CLOCK_REALTIME) Passed"); - } else { - tst_resm(TFAIL | TTERRNO, - "timer_getoverrun(CLOCK_REALTIME) Failed"); - } + if (tst_syscall(__NR_timer_delete, timer)) + tst_brk(TBROK | TERRNO, "timer_delete() failed"); - TEST(tst_syscall(__NR_timer_getoverrun, -1)); - if (TEST_RETURN == -1 && TEST_ERRNO == EINVAL) { - tst_resm(TPASS, "timer_gettime(-1) Failed: EINVAL"); - } else { - tst_resm(TFAIL | TTERRNO, - "timer_gettime(-1) = %li", TEST_RETURN); - } - } - - cleanup(); - tst_exit(); + TST_EXP_FAIL(tst_syscall(__NR_timer_getoverrun, timer), EINVAL); } + +static struct tst_test test = { + .test_all = run, +}; diff --git a/testcases/kernel/syscalls/timer_settime/timer_settime01.c b/testcases/kernel/syscalls/timer_settime/timer_settime01.c index 5aee8b38..f8e7ffa2 100755 --- a/testcases/kernel/syscalls/timer_settime/timer_settime01.c +++ b/testcases/kernel/syscalls/timer_settime/timer_settime01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2003. All Rights Reserved. * @@ -58,14 +58,18 @@ static struct time64_variants variants[] = { static volatile int caught_signal; -static void clear_signal(void) +static void clear_signal(clock_t clock, const struct tst_ts *exptime) { + struct time64_variants *tv = &variants[tst_variant]; + struct tst_ts curtime = { .type = tv->ts_type }; + /* * The busy loop is intentional. The signal is sent after X * seconds of CPU time has been accumulated for the process and * thread specific clocks. */ - while (!caught_signal); + while (!caught_signal) + ; if (caught_signal != SIGALRM) { tst_res(TFAIL, "Received incorrect signal: %s", @@ -73,6 +77,17 @@ static void clear_signal(void) } caught_signal = 0; + + if (tv->clock_gettime(clock, tst_ts_get(&curtime)) < 0) { + tst_res(TFAIL, "clock_gettime(%s) failed", + get_clock_str(clock)); + return; + } + + if (tst_ts_lt(curtime, *exptime)) { + tst_res(TFAIL, "Timer %s expired too early", + get_clock_str(clock)); + } } static void sighandler(int sig) @@ -115,23 +130,23 @@ static void run(unsigned int n) memset(&new_set, 0, sizeof(new_set)); memset(&old_set, 0, sizeof(old_set)); - new_set.type = old_set.type = tv->ts_type; + new_set.type = old_set.type = timenow.type = tv->ts_type; val = tc->it_value_tv_usec; - if (tc->flag & TIMER_ABSTIME) { - timenow.type = tv->ts_type; - if (tv->clock_gettime(clock, tst_ts_get(&timenow)) < 0) { - tst_res(TFAIL, - "clock_gettime(%s) failed - skipping the test", - get_clock_str(clock)); - continue; - } - tst_ts_add_us(timenow, val); - tst_its_set_value_from_ts(&new_set, timenow); - } else { - tst_its_set_value_from_us(&new_set, val); + if (tv->clock_gettime(clock, tst_ts_get(&timenow)) < 0) { + tst_res(TFAIL, + "clock_gettime(%s) failed - skipping the test", + get_clock_str(clock)); + continue; } + timenow = tst_ts_add_us(timenow, val); + + if (tc->flag & TIMER_ABSTIME) + tst_its_set_value_from_ts(&new_set, timenow); + else + tst_its_set_value_from_us(&new_set, val); + tst_its_set_interval_from_us(&new_set, tc->it_interval_tv_usec); TEST(tv->timer_settime(timer, tc->flag, tst_its_get(&new_set), tst_its_get(tc->old_ptr))); @@ -156,11 +171,14 @@ static void run(unsigned int n) tst_its_get_value_nsec(new_set)); } - clear_signal(); + clear_signal(clock, &timenow); /* Wait for another event when interval was set */ - if (tc->it_interval_tv_usec) - clear_signal(); + if (tc->it_interval_tv_usec) { + timenow = tst_ts_add_us(timenow, + tc->it_interval_tv_usec); + clear_signal(clock, &timenow); + } tst_res(TPASS, "timer_settime(%s) passed", get_clock_str(clock)); diff --git a/testcases/kernel/syscalls/timer_settime/timer_settime02.c b/testcases/kernel/syscalls/timer_settime/timer_settime02.c index 3309a81a..de860761 100755 --- a/testcases/kernel/syscalls/timer_settime/timer_settime02.c +++ b/testcases/kernel/syscalls/timer_settime/timer_settime02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2003. All Rights Reserved. * @@ -29,10 +29,11 @@ #include "tst_timer.h" static struct tst_its new_set, old_set; -static struct tst_its *pnew_set = &new_set, *pold_set = &old_set, *null_set = NULL; +static struct tst_its *pnew_set = &new_set, *pold_set = &old_set, *null_set; static void *faulty_set; static kernel_timer_t timer; static kernel_timer_t timer_inval = (kernel_timer_t)-1; +static volatile int caught_sig; /* separate description-array to (hopefully) improve readability */ static const char * const descriptions[] = { @@ -69,10 +70,16 @@ static struct time64_variants variants[] = { #endif }; +static void sighandler(int sig) +{ + caught_sig = sig; +} + static void setup(void) { tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc); faulty_set = tst_get_bad_addr(NULL); + signal(SIGALRM, sighandler); } static void run(unsigned int n) @@ -87,6 +94,8 @@ static void run(unsigned int n) for (i = 0; i < CLOCKS_DEFINED; ++i) { clock_t clock = clock_list[i]; + caught_sig = 0; + /* Init temporary timer */ TEST(tst_syscall(__NR_timer_create, clock, NULL, &timer)); if (TST_RET != 0) { @@ -131,6 +140,12 @@ static void run(unsigned int n) TEST(tst_syscall(__NR_timer_delete, timer)); if (TST_RET != 0) tst_res(TFAIL | TTERRNO, "timer_delete() failed!"); + + if (caught_sig) { + tst_res(TFAIL, + "Caught unexpected signal %s while testing %s", + tst_strsig(caught_sig), get_clock_str(clock)); + } } } diff --git a/testcases/kernel/syscalls/timer_settime/timer_settime03.c b/testcases/kernel/syscalls/timer_settime/timer_settime03.c index a828f63d..005bcf4c 100755 --- a/testcases/kernel/syscalls/timer_settime/timer_settime03.c +++ b/testcases/kernel/syscalls/timer_settime/timer_settime03.c @@ -67,7 +67,7 @@ static void setup(void) SAFE_CLOCK_GETRES(CLOCK_REALTIME, &realtime_resolution); tst_res(TINFO, "CLOCK_REALTIME resolution %lins", - (long)realtime_resolution.tv_nsec); + (long)realtime_resolution.tv_nsec); } static void run(void) @@ -93,7 +93,9 @@ static void run(void) spec.it_interval.tv_nsec = realtime_resolution.tv_nsec; SAFE_TIMER_SETTIME(timer, TIMER_ABSTIME, &spec, NULL); - while (!handler_called); + while (!handler_called) + ; + errno = saved_errno; if (overrun == -1) diff --git a/testcases/kernel/syscalls/timerfd/.gitignore b/testcases/kernel/syscalls/timerfd/.gitignore index ef388685..ca6cbb1f 100755 --- a/testcases/kernel/syscalls/timerfd/.gitignore +++ b/testcases/kernel/syscalls/timerfd/.gitignore @@ -1,6 +1,5 @@ /timerfd01 /timerfd02 -/timerfd03 /timerfd04 /timerfd_create01 /timerfd_gettime01 diff --git a/testcases/kernel/syscalls/timerfd/timerfd02.c b/testcases/kernel/syscalls/timerfd/timerfd02.c index 936cdbc5..960ce63b 100755 --- a/testcases/kernel/syscalls/timerfd/timerfd02.c +++ b/testcases/kernel/syscalls/timerfd/timerfd02.c @@ -1,174 +1,51 @@ -/******************************************************************************/ -/* */ -/* Copyright (c) Ulrich Drepper */ -/* Copyright (c) International Business Machines Corp., 2009 */ -/* */ -/* 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 */ -/* */ -/******************************************************************************/ -/******************************************************************************/ -/* */ -/* File: timerfd02.c */ -/* */ -/* Description: This Program tests the new system call introduced in 2.6.27. */ -/* Ulrich´s comment as in: */ -/* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=11fcb6c14676023d0bd437841f5dcd670e7990a0 */ -/* says: */ -/* The timerfd_create syscall already has a flags parameter. It just is */ -/* unused so far. This patch changes this by introducing the TFD_CLOEXEC */ -/* flag to set the close-on-exec flag for the returned file descriptor. A new */ -/* name TFD_CLOEXEC is introduced which in this implementation must have the */ -/* same value as O_CLOEXEC. */ -/* The following test must be adjusted for architectures other than x86 and */ -/* x86-64 and in case the syscall numbers changed. */ -/* */ -/* Usage: */ -/* timerfd02 [-c n] [-e][-i n] [-I x] [-p x] [-t] */ -/* where, -c n : Run n copies concurrently. */ -/* -e : Turn on errno logging. */ -/* -i n : Execute test n times. */ -/* -I x : Execute test for x seconds. */ -/* -P x : Pause for x seconds between iterations. */ -/* -t : Turn on syscall timing. */ -/* */ -/* Total Tests: 1 */ -/* */ -/* Test Name: timerfd02 */ -/* */ -/* Author: Ulrich Drepper */ -/* */ -/* History: Created - Jan 08 2009 - Ulrich Drepper */ -/* Ported to LTP */ -/* - Jan 08 2009 - Subrata */ -/******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Ulrich Drepper + * Copyright (c) International Business Machines Corp., 2009 + * Copyright (C) 2023 SUSE LLC Andrea Cervesato + */ -#include -#include -#include -#include -#include +/*\ + * This test verifies that: + * + * - TFD_CLOEXEC sets the close-on-exec file status flag on the new open file + * - TFD_NONBLOCK sets the O_NONBLOCK file status flag on the new open file + */ -#include "test.h" +#include "tst_test.h" +#include "tst_safe_timerfd.h" #include "lapi/fcntl.h" #include "lapi/syscalls.h" -#define TFD_CLOEXEC O_CLOEXEC +static int fdesc; -char *TCID = "timerfd02"; -int testno; -int TST_TOTAL = 1; +static struct test_case_t { + int flags; + int check; + int expected; +} tcases[] = { + { 0, F_GETFD, 0 }, + { TFD_CLOEXEC, F_GETFD, FD_CLOEXEC }, + { TFD_NONBLOCK, F_GETFL, O_NONBLOCK } +}; -/* Extern Global Functions */ -/******************************************************************************/ -/* */ -/* Function: cleanup */ -/* */ -/* Description: Performs all one time clean up for this test on successful */ -/* completion, premature exit or failure. Closes all temporary */ -/* files, removes all temporary directories exits the test with */ -/* appropriate return code by calling tst_exit() function. */ -/* */ -/* Input: None. */ -/* */ -/* Output: None. */ -/* */ -/* Return: On failure - Exits calling tst_exit(). Non '0' return code. */ -/* On success - Exits calling tst_exit(). With '0' return code. */ -/* */ -/******************************************************************************/ -void cleanup(void) +static void run(unsigned int i) { + struct test_case_t *tcase = &tcases[i]; - tst_rmdir(); - + TST_EXP_FD(fdesc = SAFE_TIMERFD_CREATE(CLOCK_REALTIME, tcase->flags)); + TST_EXP_EQ_LI(SAFE_FCNTL(fdesc, tcase->check) & tcase->expected, tcase->expected); + SAFE_CLOSE(fdesc); } -/* Local Functions */ -/******************************************************************************/ -/* */ -/* Function: setup */ -/* */ -/* Description: Performs all one time setup for this test. This function is */ -/* typically used to capture signals, create temporary dirs */ -/* and temporary files that may be used in the course of this */ -/* test. */ -/* */ -/* Input: None. */ -/* */ -/* Output: None. */ -/* */ -/* Return: On failure - Exits by calling cleanup(). */ -/* On success - returns 0. */ -/* */ -/******************************************************************************/ -void setup(void) +static void cleanup(void) { - /* Capture signals if any */ - /* Create temporary directories */ - TEST_PAUSE; - tst_tmpdir(); + if (fcntl(fdesc, F_GETFD) != -1) + SAFE_CLOSE(fdesc); } -int main(int argc, char *argv[]) -{ - int fd, coe; - int lc; - - tst_parse_opts(argc, argv, NULL, NULL); - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - tst_count = 0; - for (testno = 0; testno < TST_TOTAL; ++testno) { - fd = tst_syscall(__NR_timerfd_create, - CLOCK_REALTIME, 0); - if (fd == -1) { - tst_brkm(TFAIL, cleanup, - "timerfd_create(0) failed"); - } - coe = fcntl(fd, F_GETFD); - if (coe == -1) { - tst_brkm(TBROK, cleanup, "fcntl failed"); - } - if (coe & FD_CLOEXEC) { - tst_brkm(TFAIL, - cleanup, - "timerfd_create(0) set close-on-exec flag"); - } - close(fd); - - fd = tst_syscall(__NR_timerfd_create, CLOCK_REALTIME, - TFD_CLOEXEC); - if (fd == -1) { - tst_brkm(TFAIL, - cleanup, - "timerfd_create(TFD_CLOEXEC) failed"); - } - coe = fcntl(fd, F_GETFD); - if (coe == -1) { - tst_brkm(TBROK, cleanup, "fcntl failed"); - } - if ((coe & FD_CLOEXEC) == 0) { - tst_brkm(TFAIL, - cleanup, - "timerfd_create(TFD_CLOEXEC) set close-on-exec flag"); - } - close(fd); - tst_resm(TPASS, "timerfd_create(TFD_CLOEXEC) Passed"); - cleanup(); - } - } - tst_exit(); -} +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(tcases), + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/timerfd/timerfd03.c b/testcases/kernel/syscalls/timerfd/timerfd03.c deleted file mode 100755 index 89dec325..00000000 --- a/testcases/kernel/syscalls/timerfd/timerfd03.c +++ /dev/null @@ -1,170 +0,0 @@ -/******************************************************************************/ -/* */ -/* Copyright (c) Ulrich Drepper */ -/* Copyright (c) International Business Machines Corp., 2009 */ -/* */ -/* 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 */ -/* */ -/******************************************************************************/ -/******************************************************************************/ -/* */ -/* File: timerfd03.c */ -/* */ -/* Description: This Program tests the new system call introduced in 2.6.27. */ -/* Ulrich´s comment as in: */ -/* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=6b1ef0e60d42f2fdaec26baee8327eb156347b4f */ -/* which says: */ -/* This patch adds support for the TFD_NONBLOCK flag to timerfd_create. The */ -/* additional changes needed are minimal. The following test must be adjusted */ -/* for architectures other than x86 and x86-64 and in case the syscall numbers*/ -/* changed. */ -/* */ -/* Usage: */ -/* timerfd03 [-c n] [-e][-i n] [-I x] [-p x] [-t] */ -/* where, -c n : Run n copies concurrently. */ -/* -e : Turn on errno logging. */ -/* -i n : Execute test n times. */ -/* -I x : Execute test for x seconds. */ -/* -P x : Pause for x seconds between iterations. */ -/* -t : Turn on syscall timing. */ -/* */ -/* Total Tests: 1 */ -/* */ -/* Test Name: timerfd03 */ -/* */ -/* Author: Ulrich Drepper */ -/* */ -/* History: Created - Jan 13 2009 - Ulrich Drepper */ -/* Ported to LTP */ -/* - Jan 13 2009 - Subrata */ -/******************************************************************************/ -#include -#include -#include -#include -#include - -#include "test.h" -#include "lapi/fcntl.h" -#include "lapi/syscalls.h" - -#define TFD_NONBLOCK O_NONBLOCK - -char *TCID = "timerfd03"; -int testno; -int TST_TOTAL = 1; - -/* Extern Global Functions */ -/******************************************************************************/ -/* */ -/* Function: cleanup */ -/* */ -/* Description: Performs all one time clean up for this test on successful */ -/* completion, premature exit or failure. Closes all temporary */ -/* files, removes all temporary directories exits the test with */ -/* appropriate return code by calling tst_exit() function. */ -/* */ -/* Input: None. */ -/* */ -/* Output: None. */ -/* */ -/* Return: On failure - Exits calling tst_exit(). Non '0' return code. */ -/* On success - Exits calling tst_exit(). With '0' return code. */ -/* */ -/******************************************************************************/ -void cleanup(void) -{ - - tst_rmdir(); - -} - -/* Local Functions */ -/******************************************************************************/ -/* */ -/* Function: setup */ -/* */ -/* Description: Performs all one time setup for this test. This function is */ -/* typically used to capture signals, create temporary dirs */ -/* and temporary files that may be used in the course of this */ -/* test. */ -/* */ -/* Input: None. */ -/* */ -/* Output: None. */ -/* */ -/* Return: On failure - Exits by calling cleanup(). */ -/* On success - returns 0. */ -/* */ -/******************************************************************************/ -void setup(void) -{ - /* Capture signals if any */ - /* Create temporary directories */ - TEST_PAUSE; - tst_tmpdir(); -} - -int main(int argc, char *argv[]) -{ - int fd, fl; - int lc; - - tst_parse_opts(argc, argv, NULL, NULL); - setup(); - - for (lc = 0; TEST_LOOPING(lc); ++lc) { - tst_count = 0; - for (testno = 0; testno < TST_TOTAL; ++testno) { - fd = tst_syscall(__NR_timerfd_create, - CLOCK_REALTIME, 0); - if (fd == -1) { - tst_brkm(TFAIL, cleanup, - "timerfd_create(0) failed"); - } - fl = fcntl(fd, F_GETFL); - if (fl == -1) { - tst_brkm(TBROK, cleanup, "fcntl failed"); - } - if (fl & O_NONBLOCK) { - tst_brkm(TFAIL, - cleanup, - "timerfd_create(0) set non-blocking mode"); - } - close(fd); - - fd = tst_syscall(__NR_timerfd_create, CLOCK_REALTIME, - TFD_NONBLOCK); - if (fd == -1) { - tst_brkm(TFAIL, - cleanup, - "timerfd_create(TFD_NONBLOCK) failed"); - } - fl = fcntl(fd, F_GETFL); - if (fl == -1) { - tst_brkm(TBROK, cleanup, "fcntl failed"); - } - if ((fl & O_NONBLOCK) == 0) { - tst_brkm(TFAIL, - cleanup, - "timerfd_create(TFD_NONBLOCK) set non-blocking mode"); - } - close(fd); - tst_resm(TPASS, "timerfd_create(TFD_NONBLOCK) PASSED"); - cleanup(); - } - } - tst_exit(); -} diff --git a/testcases/kernel/syscalls/timerfd/timerfd_create01.c b/testcases/kernel/syscalls/timerfd/timerfd_create01.c index 18a23358..2a658f06 100755 --- a/testcases/kernel/syscalls/timerfd/timerfd_create01.c +++ b/testcases/kernel/syscalls/timerfd/timerfd_create01.c @@ -5,8 +5,6 @@ * Copyright (C) 2023 SUSE LLC Andrea Cervesato */ /*\ - * [Description] - * * This test verifies that: * - clockid argument is neither CLOCK_MONOTONIC nor CLOCK_REALTIME, * EINVAL would return. diff --git a/testcases/kernel/syscalls/timerfd/timerfd_settime02.c b/testcases/kernel/syscalls/timerfd/timerfd_settime02.c index 33d9f7b4..a5a6573e 100755 --- a/testcases/kernel/syscalls/timerfd/timerfd_settime02.c +++ b/testcases/kernel/syscalls/timerfd/timerfd_settime02.c @@ -111,7 +111,8 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .taint_check = TST_TAINT_W | TST_TAINT_D, - .max_runtime = 150, + .runtime = 150, + .min_runtime = 2, .tags = (const struct tst_tag[]) { {"linux-git", "1e38da300e1e"}, {"CVE", "2017-10661"}, diff --git a/testcases/kernel/syscalls/times/times01.c b/testcases/kernel/syscalls/times/times01.c index 408b91c9..4c809aa6 100755 --- a/testcases/kernel/syscalls/times/times01.c +++ b/testcases/kernel/syscalls/times/times01.c @@ -3,8 +3,6 @@ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. */ /*\ - * [Description] - * * This is a Phase I test for the times(2) system call. It is intended to * provide a limited exposure of the system call. */ diff --git a/testcases/kernel/syscalls/tkill/tkill01.c b/testcases/kernel/syscalls/tkill/tkill01.c index 15f2dfb3..d97ed0b9 100755 --- a/testcases/kernel/syscalls/tkill/tkill01.c +++ b/testcases/kernel/syscalls/tkill/tkill01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Basic tests for the tkill syscall. * * [Algorithm] diff --git a/testcases/kernel/syscalls/tkill/tkill02.c b/testcases/kernel/syscalls/tkill/tkill02.c index 6cd60923..30823f61 100755 --- a/testcases/kernel/syscalls/tkill/tkill02.c +++ b/testcases/kernel/syscalls/tkill/tkill02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic tests for the tkill() errors. * * [Algorithm] diff --git a/testcases/kernel/syscalls/truncate/truncate02.c b/testcases/kernel/syscalls/truncate/truncate02.c index 8d7f9a6f..2ed6f18e 100755 --- a/testcases/kernel/syscalls/truncate/truncate02.c +++ b/testcases/kernel/syscalls/truncate/truncate02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that: * * - truncate(2) truncates a file to a specified length successfully. diff --git a/testcases/kernel/syscalls/truncate/truncate03.c b/testcases/kernel/syscalls/truncate/truncate03.c index 2607e7d6..a017ee4b 100755 --- a/testcases/kernel/syscalls/truncate/truncate03.c +++ b/testcases/kernel/syscalls/truncate/truncate03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that: * * - truncate(2) returns -1 and sets errno to EACCES if search/write diff --git a/testcases/kernel/syscalls/umount/umount01.c b/testcases/kernel/syscalls/umount/umount01.c index d05296dc..a8f34e7a 100755 --- a/testcases/kernel/syscalls/umount/umount01.c +++ b/testcases/kernel/syscalls/umount/umount01.c @@ -2,12 +2,13 @@ /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Author: Nirmala Devi Dhanasekar - * - * Phase I test for the umount(2) system call. - * It is intended to provide a limited exposure of the system call. + * Copyright (c) Linux Test Project, 2002-2023 + */ + +/*\ + * Check the basic functionality of the umount(2) system call. */ -#include #include #include "tst_test.h" @@ -23,7 +24,7 @@ static void verify_umount(void) mount_flag = 1; } - TEST(umount(MNTPOINT)); + TST_EXP_PASS(umount(MNTPOINT)); if (TST_RET != 0 && TST_ERR == EBUSY) { tst_res(TINFO, "umount() Failed with EBUSY " @@ -31,12 +32,6 @@ static void verify_umount(void) "is probing newly mounted dirs"); } - if (TST_RET != 0) { - tst_res(TFAIL | TTERRNO, "umount() Failed"); - return; - } - - tst_res(TPASS, "umount() Passed"); mount_flag = 0; } diff --git a/testcases/kernel/syscalls/umount/umount02.c b/testcases/kernel/syscalls/umount/umount02.c index 34a38c99..e7fe246e 100755 --- a/testcases/kernel/syscalls/umount/umount02.c +++ b/testcases/kernel/syscalls/umount/umount02.c @@ -2,20 +2,22 @@ /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * Copyright (c) 2014 Cyril Hrubis + * Copyright (c) Linux Test Project, 2002-2023 * Author: Nirmala Devi Dhanasekar - * + */ + +/*\ * Check for basic errors returned by umount(2) system call. * * Verify that umount(2) returns -1 and sets errno to - * 1) EBUSY if it cannot be umounted, because dir is still busy. - * 2) EFAULT if specialfile or device file points to invalid address space. - * 3) ENOENT if pathname was empty or has a nonexistent component. - * 4) EINVAL if specialfile or device is invalid or not a mount point. - * 5) ENAMETOOLONG if pathname was longer than MAXPATHLEN. + * + * 1. EBUSY if it cannot be umounted, because dir is still busy. + * 2. EFAULT if specialfile or device file points to invalid address space. + * 3. ENOENT if pathname was empty or has a nonexistent component. + * 4. EINVAL if specialfile or device is invalid or not a mount point. + * 5. ENAMETOOLONG if pathname was longer than MAXPATHLEN. */ -#include -#include #include #include "tst_test.h" @@ -41,21 +43,7 @@ static void verify_umount(unsigned int n) { struct tcase *tc = &tcases[n]; - TEST(umount(tc->mntpoint)); - - if (TST_RET != -1) { - tst_res(TFAIL, "umount() succeeds unexpectedly"); - return; - } - - if (tc->exp_errno != TST_ERR) { - tst_res(TFAIL | TTERRNO, "umount() should fail with %s", - tst_strerrno(tc->exp_errno)); - return; - } - - tst_res(TPASS | TTERRNO, "umount() fails as expected: %s", - tc->err_desc); + TST_EXP_FAIL(umount(tc->mntpoint), tc->exp_errno); } static void setup(void) diff --git a/testcases/kernel/syscalls/umount/umount03.c b/testcases/kernel/syscalls/umount/umount03.c index 1cef06fa..c181e63d 100755 --- a/testcases/kernel/syscalls/umount/umount03.c +++ b/testcases/kernel/syscalls/umount/umount03.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. + * Copyright (c) Linux Test Project, 2002-2023 * Author: Nirmala Devi Dhanasekar - * - * Verify that umount(2) returns -1 and sets errno to EPERM if the user + */ + +/*\ + * Verify that umount(2) returns -1 and sets errno to EPERM if the user * is not the super-user. */ -#include #include #include -#include -#include #include "tst_test.h" #define MNTPOINT "mntpoint" @@ -20,19 +20,7 @@ static int mount_flag; static void verify_umount(void) { - TEST(umount(MNTPOINT)); - - if (TST_RET != -1) { - tst_res(TFAIL, "umount() succeeds unexpectedly"); - return; - } - - if (TST_ERR != EPERM) { - tst_res(TFAIL | TTERRNO, "umount() should fail with EPERM"); - return; - } - - tst_res(TPASS | TTERRNO, "umount() fails as expected"); + TST_EXP_FAIL(umount(MNTPOINT), EPERM); } static void setup(void) diff --git a/testcases/kernel/syscalls/umount2/umount2_02.c b/testcases/kernel/syscalls/umount2/umount2_02.c index f4b228f9..ff0bdf91 100755 --- a/testcases/kernel/syscalls/umount2/umount2_02.c +++ b/testcases/kernel/syscalls/umount2/umount2_02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test for feature MNT_EXPIRE of umount2(): * * - EINVAL when flag is specified with either MNT_FORCE or MNT_DETACH diff --git a/testcases/kernel/syscalls/unlink/.gitignore b/testcases/kernel/syscalls/unlink/.gitignore index 2e783580..4fc24059 100755 --- a/testcases/kernel/syscalls/unlink/.gitignore +++ b/testcases/kernel/syscalls/unlink/.gitignore @@ -1,3 +1,5 @@ /unlink05 /unlink07 /unlink08 +/unlink09 +/unlink10 diff --git a/testcases/kernel/syscalls/unlink/unlink05.c b/testcases/kernel/syscalls/unlink/unlink05.c index adb90fa6..b2702696 100755 --- a/testcases/kernel/syscalls/unlink/unlink05.c +++ b/testcases/kernel/syscalls/unlink/unlink05.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) Linux Test Project, 2006-2024 */ -/* - * Description: - * The testcase checks the basic functionality of the unlink(2). - * 1) unlink() can delete regular file successfully. - * 2) unlink() can delete fifo file successfully. +/*\ + * Check the basic functionality of the unlink(2): + * + * - unlink(2) can delete regular file successfully. + * - unlink(2) can delete fifo file successfully. */ #include @@ -16,17 +17,6 @@ #include #include "tst_test.h" -static void file_create(char *); -static void fifo_create(char *); - -static struct test_case_t { - void (*setupfunc)(char *); - char *desc; -} tcases[] = { - {file_create, "file"}, - {fifo_create, "fifo"}, -}; - static void file_create(char *name) { sprintf(name, "tfile_%d", getpid()); @@ -39,6 +29,14 @@ static void fifo_create(char *name) SAFE_MKFIFO(name, 0777); } +static struct test_case_t { + void (*setupfunc)(char *name); + char *desc; +} tcases[] = { + {file_create, "file"}, + {fifo_create, "fifo"}, +}; + static void verify_unlink(unsigned int n) { char fname[255]; diff --git a/testcases/kernel/syscalls/unlink/unlink07.c b/testcases/kernel/syscalls/unlink/unlink07.c index b7bbd8d1..240ab5cc 100755 --- a/testcases/kernel/syscalls/unlink/unlink07.c +++ b/testcases/kernel/syscalls/unlink/unlink07.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that unlink(2) fails with * * - ENOENT when file does not exist @@ -14,7 +12,7 @@ * - ENOENT when a component in pathname does not exist * - EFAULT when pathname points outside the accessible address space * - ENOTDIR when a component used as a directory in pathname is not, - * in fact, a directory + * in fact, a directory * - ENAMETOOLONG when pathname is too long */ diff --git a/testcases/kernel/syscalls/unlink/unlink08.c b/testcases/kernel/syscalls/unlink/unlink08.c index 1fe6e89f..6d3ab261 100755 --- a/testcases/kernel/syscalls/unlink/unlink08.c +++ b/testcases/kernel/syscalls/unlink/unlink08.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that unlink(2) fails with * * - EACCES when no write access to the directory containing pathname diff --git a/testcases/kernel/syscalls/unlink/unlink09.c b/testcases/kernel/syscalls/unlink/unlink09.c new file mode 100644 index 00000000..e2ee062c --- /dev/null +++ b/testcases/kernel/syscalls/unlink/unlink09.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Author: Yang Xu + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that unlink(2) fails with EPERM when target file is marked as + * immutable or append-only. + */ + +#include +#include "tst_test.h" +#include "lapi/fs.h" + +#define MNTPOINT "mnt" +#define TEST_EPERM_IMMUTABLE MNTPOINT"/test_eperm_immutable" +#define TEST_EPERM_APPEND_ONLY MNTPOINT"/test_eperm_append_only" + +static int fd_immutable = -1; +static int fd_append_only = -1; + +static struct test_case_t { + char *filename; + int *fd; + int flag; + char *desc; +} tcases[] = { + {TEST_EPERM_IMMUTABLE, &fd_immutable, FS_IMMUTABLE_FL, + "target file is immutable"}, + {TEST_EPERM_APPEND_ONLY, &fd_append_only, FS_APPEND_FL, + "target file is append-only"}, +}; + +static void setup_inode_flag(const int fd, const int flag, const int set) +{ + int attr; + + SAFE_IOCTL(fd, FS_IOC_GETFLAGS, &attr); + + if (set) + attr |= flag; + else + attr &= ~flag; + + SAFE_IOCTL(fd, FS_IOC_SETFLAGS, &attr); +} + +static void setup(void) +{ + int attr; + + /* inode attributes in tmpfs are supported from kernel 6.0 + * https://lore.kernel.org/all/20220715015912.2560575-1-tytso@mit.edu/ + */ + if (!strcmp(tst_device->fs_type, "tmpfs") && tst_kvercmp(6, 0, 0) < 0) + tst_brk(TCONF, "FS_IOC_GETFLAGS on tmpfs not supported for kernel<6.0"); + + fd_immutable = SAFE_CREAT(TEST_EPERM_IMMUTABLE, 0600); + TEST(ioctl(fd_immutable, FS_IOC_GETFLAGS, &attr)); + + if (TST_RET == -1 && TST_ERR == ENOTTY) { + SAFE_CLOSE(fd_immutable); + + tst_brk(TBROK, "Inode attributes not supported by '%s'", + tst_device->fs_type); + } + + attr |= FS_IMMUTABLE_FL; + SAFE_IOCTL(fd_immutable, FS_IOC_SETFLAGS, &attr); + + fd_append_only = SAFE_CREAT(TEST_EPERM_APPEND_ONLY, 0600); + setup_inode_flag(fd_append_only, FS_APPEND_FL, 1); +} + +static void cleanup(void) +{ + if (fd_immutable != -1) { + setup_inode_flag(fd_immutable, FS_IMMUTABLE_FL, 0); + SAFE_CLOSE(fd_immutable); + } + + if (fd_append_only != -1) { + setup_inode_flag(fd_append_only, FS_APPEND_FL, 0); + SAFE_CLOSE(fd_append_only); + } +} + +static void verify_unlink(unsigned int i) +{ + struct test_case_t *tc = &tcases[i]; + + TST_EXP_FAIL(unlink(tc->filename), EPERM, "%s", tc->desc); + + /* If unlink() succeeded unexpectedly, test file should be restored. */ + if (!TST_RET) { + *(tc->fd) = SAFE_CREAT(tc->filename, 0600); + setup_inode_flag(*(tc->fd), tc->flag, 1); + } +} + +static struct tst_test test = { + .setup = setup, + .tcnt = ARRAY_SIZE(tcases), + .cleanup = cleanup, + .test = verify_unlink, + .mntpoint = MNTPOINT, + .needs_root = 1, + .mount_device = 1, + .all_filesystems = 1, + .skip_filesystems = (const char *const[]) { + "fuse", + "exfat", + "vfat", + "ntfs", + NULL + }, +}; diff --git a/testcases/kernel/syscalls/unlink/unlink10.c b/testcases/kernel/syscalls/unlink/unlink10.c new file mode 100644 index 00000000..453dd5c7 --- /dev/null +++ b/testcases/kernel/syscalls/unlink/unlink10.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 FUJITSU LIMITED. All Rights Reserved. + * Author: Yang Xu + * Copyright (C) 2024 SUSE LLC Andrea Cervesato + */ + +/*\ + * Verify that unlink(2) fails with EROFS when target file is on a read-only + * filesystem. + */ + +#include +#include "tst_test.h" +#include "lapi/fs.h" + +#define MNTPOINT "erofs" +#define FILENAME MNTPOINT"/file" + +static void run(void) +{ + TST_EXP_FAIL(unlink(FILENAME), EROFS, + "%s", "target file in read-only filesystem"); +} + +static struct tst_test test = { + .test_all = run, + .needs_rofs = 1, + .needs_root = 1, + .mntpoint = MNTPOINT, +}; diff --git a/testcases/kernel/syscalls/unlinkat/unlinkat01.c b/testcases/kernel/syscalls/unlinkat/unlinkat01.c index 7dba1d64..9374466f 100755 --- a/testcases/kernel/syscalls/unlinkat/unlinkat01.c +++ b/testcases/kernel/syscalls/unlinkat/unlinkat01.c @@ -7,7 +7,6 @@ */ /*\ - * [Description] * Basic unlinkat() test. */ diff --git a/testcases/kernel/syscalls/unshare/.gitignore b/testcases/kernel/syscalls/unshare/.gitignore index 855ffd05..8ece5f98 100755 --- a/testcases/kernel/syscalls/unshare/.gitignore +++ b/testcases/kernel/syscalls/unshare/.gitignore @@ -1,2 +1,5 @@ /unshare01 /unshare02 +/unshare03 +/unshare04 +/unshare05 diff --git a/testcases/kernel/syscalls/unshare/unshare01.c b/testcases/kernel/syscalls/unshare/unshare01.c index ad26a908..c86ea9d2 100755 --- a/testcases/kernel/syscalls/unshare/unshare01.c +++ b/testcases/kernel/syscalls/unshare/unshare01.c @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Crackerjack Project., 2007 + * Copyright (c) Linux Test Project, 2009-2025 * Ported from Crackerjack to LTP by Manas Kumar Nayak maknayak@in.ibm.com> */ /*\ - * [Description] - * * Basic tests for the unshare() syscall. * * [Algorithm] @@ -31,19 +30,20 @@ #ifdef HAVE_UNSHARE +#define FLAG_DESC(x) .mode = x, .desc = #x + static struct test_case_t { int mode; const char *desc; } tc[] = { - {CLONE_FILES, "CLONE_FILES"}, - {CLONE_FS, "CLONE_FS"}, - {CLONE_NEWNS, "CLONE_NEWNS"}, + {FLAG_DESC(CLONE_FILES)}, + {FLAG_DESC(CLONE_FS)}, + {FLAG_DESC(CLONE_NEWNS)}, }; static void run(unsigned int i) { - pid_t pid = SAFE_FORK(); - if (pid == 0) + if (!SAFE_FORK()) TST_EXP_PASS(unshare(tc[i].mode), "unshare(%s)", tc[i].desc); } diff --git a/testcases/kernel/syscalls/unshare/unshare02.c b/testcases/kernel/syscalls/unshare/unshare02.c index 3783e8e9..436b0c3b 100755 --- a/testcases/kernel/syscalls/unshare/unshare02.c +++ b/testcases/kernel/syscalls/unshare/unshare02.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Basic tests for the unshare(2) errors. * * - EINVAL on invalid flags diff --git a/testcases/kernel/syscalls/unshare/unshare03.c b/testcases/kernel/syscalls/unshare/unshare03.c new file mode 100644 index 00000000..7128b3b9 --- /dev/null +++ b/testcases/kernel/syscalls/unshare/unshare03.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Al Viro + * Copyright (C) 2024 Wei Gao + */ + +/*\ + * This test case based on kernel self-test unshare_test.c to check that + * the kernel handles the EMFILE error when a parent process changes file + * descriptor limits and the child process tries to unshare (CLONE_FILES). + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "config.h" +#include "lapi/sched.h" + +#define FS_NR_OPEN "/proc/sys/fs/nr_open" + +#ifdef HAVE_UNSHARE + +static void run(void) +{ + struct tst_clone_args args = { + .flags = CLONE_FILES, + .exit_signal = SIGCHLD, + }; + + int nr_open = sizeof(long long) * 8; + + SAFE_DUP2(2, nr_open + 1); + + if (!SAFE_CLONE(&args)) { + SAFE_FILE_PRINTF(FS_NR_OPEN, "%d", nr_open); + TST_EXP_FAIL(unshare(CLONE_FILES), EMFILE); + exit(0); + } +} + +static void setup(void) +{ + clone3_supported_by_kernel(); +} + +static struct tst_test test = { + .forks_child = 1, + .needs_root = 1, + .test_all = run, + .setup = setup, + .save_restore = (const struct tst_path_val[]) { + {FS_NR_OPEN, NULL, TST_SR_TCONF}, + {} + }, +}; + +#else +TST_TEST_TCONF("unshare syscall is undefined."); +#endif diff --git a/testcases/kernel/syscalls/unshare/unshare04.c b/testcases/kernel/syscalls/unshare/unshare04.c new file mode 100644 index 00000000..4305c5cb --- /dev/null +++ b/testcases/kernel/syscalls/unshare/unshare04.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 lufei + */ + +/*\ + * This test case is to verify unshare(CLONE_NEWNS) also unshares process + * working directory. + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/sched.h" + +#define TESTDIR "test_dir" + +static char *cwd; +static char *buff; +static size_t size = 1024; + +static void setup(void) +{ + cwd = SAFE_MALLOC(size); + SAFE_GETCWD(cwd, size); + + SAFE_MKDIR(TESTDIR, 0700); + + buff = SAFE_MALLOC(size); +} + +static void cleanup(void) +{ + free(buff); + free(cwd); +} + +static void run(void) +{ + struct tst_clone_args args = { + .flags = CLONE_FS, + .exit_signal = SIGCHLD, + }; + + if (!SAFE_CLONE(&args)) { + + TST_EXP_PASS(unshare(CLONE_NEWNS)); + + SAFE_CHDIR(TESTDIR); + SAFE_GETCWD(buff, size); + + if (strcmp(cwd, buff) == 0) + tst_res(TFAIL, "current dir not changed"); + else + tst_res(TPASS, "current dir changed to %s", buff); + } else { + SAFE_WAIT(NULL); + + SAFE_GETCWD(buff, size); + + if (strcmp(cwd, buff) == 0) + tst_res(TPASS, "cwd unshared"); + else + tst_res(TFAIL, "cwd not unshared as expected"); + } +} + +static struct tst_test test = { + .forks_child = 1, + .needs_root = 1, + .needs_tmpdir = 1, + .test_all = run, + .setup = setup, + .cleanup = cleanup, +}; diff --git a/testcases/kernel/syscalls/unshare/unshare05.c b/testcases/kernel/syscalls/unshare/unshare05.c new file mode 100644 index 00000000..3185d4d2 --- /dev/null +++ b/testcases/kernel/syscalls/unshare/unshare05.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 lufei + */ + +/*\ + * This test case verifies unshare(CLONE_NEWPID) creates a new PID namespace + * and that the first child process in the new namespace gets PID 1. + */ + +#define _GNU_SOURCE + +#include "tst_test.h" +#include "lapi/sched.h" + +static struct tst_clone_args *args; + +static void setup(void) +{ + args->flags = CLONE_NEWPID; + args->exit_signal = SIGCHLD; +} + +static void run(void) +{ + if (SAFE_CLONE(args)) + return; + + SAFE_UNSHARE(CLONE_NEWPID); + + if (!SAFE_FORK()) { + TST_EXP_EQ_LI(getpid(), 1); + exit(0); + } + + tst_reap_children(); +} + +static struct tst_test test = { + .setup = setup, + .forks_child = 1, + .needs_root = 1, + .test_all = run, + .bufs = (struct tst_buffers []) { + {&args, .size = sizeof(*args)}, + {}, + } +}; diff --git a/testcases/kernel/syscalls/ustat/ustat01.c b/testcases/kernel/syscalls/ustat/ustat01.c index 70a44adb..16100605 100755 --- a/testcases/kernel/syscalls/ustat/ustat01.c +++ b/testcases/kernel/syscalls/ustat/ustat01.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2003. All Rights Reserved. * diff --git a/testcases/kernel/syscalls/ustat/ustat02.c b/testcases/kernel/syscalls/ustat/ustat02.c index a5b0cc1b..84becaa1 100755 --- a/testcases/kernel/syscalls/ustat/ustat02.c +++ b/testcases/kernel/syscalls/ustat/ustat02.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. * @@ -30,9 +30,7 @@ static struct test_case_t { struct ustat *buf; } tc[] = { {"Invalid parameter", EINVAL, "EINVAL", &invalid_dev, &ubuf}, -#ifndef UCLINUX {"Bad address", EFAULT, "EFAULT", &root_dev, (void*)-1} -#endif }; int TST_TOTAL = ARRAY_SIZE(tc); diff --git a/testcases/kernel/syscalls/utils/compat_16.mk b/testcases/kernel/syscalls/utils/compat_16.mk index e81a00c4..556d87f3 100755 --- a/testcases/kernel/syscalls/utils/compat_16.mk +++ b/testcases/kernel/syscalls/utils/compat_16.mk @@ -53,26 +53,17 @@ CPPFLAGS += -I$(abs_srcdir) -I$(abs_srcdir)/../utils SRCS ?= $(sort $(wildcard $(abs_srcdir)/*.c)) MAKE_TARGETS := $(notdir $(patsubst %.c,%,$(SRCS))) -MAKE_TARGETS_OBJS_WO_COMPAT_16 := $(addsuffix .o,$(MAKE_TARGETS)) MAKE_TARGETS += $(addsuffix _16,$(MAKE_TARGETS)) -# XXX (garrcoop): This code should be put in question as it cannot be applied -# (no .h file, no TST_USE_NEWER64_SYSCALL def). DEF_16 := TST_USE_COMPAT16_SYSCALL -ifneq ($(COMPAT_TST_16_H),1) +ifeq ($(USE_LEGACY_COMPAT_16_H),1) COMPAT_16_H := $(abs_srcdir)/../utils/compat_16.h else COMPAT_16_H := $(abs_srcdir)/../utils/compat_tst_16.h endif -ifneq ($(wildcard $(COMPAT_16_H)),) -$(MAKE_TARGETS_OBJS_WO_COMPAT_16): $(COMPAT_16_H) -.INTERMEDIATE: $(MAKE_TARGETS_OBJS_WO_COMPAT_16) -endif - %_16: CPPFLAGS += -D$(DEF_16)=1 -# XXX (garrcoop): End section of code in question.. %_16.o: %.c $(COMPAT_16_H) $(COMPILE.c) $(OUTPUT_OPTION) $< diff --git a/testcases/kernel/syscalls/utime/.gitignore b/testcases/kernel/syscalls/utime/.gitignore index 94c0ae07..40376452 100755 --- a/testcases/kernel/syscalls/utime/.gitignore +++ b/testcases/kernel/syscalls/utime/.gitignore @@ -4,3 +4,4 @@ /utime04 /utime05 /utime06 +/utime07 diff --git a/testcases/kernel/syscalls/utime/utime01.c b/testcases/kernel/syscalls/utime/utime01.c index 0cc9822e..9e985ec3 100755 --- a/testcases/kernel/syscalls/utime/utime01.c +++ b/testcases/kernel/syscalls/utime/utime01.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that the system call utime() successfully changes the last * access and modification times of a file to the current time if the * times argument is NULL and the user ID of the process is "root". @@ -33,7 +31,7 @@ static void run(void) struct stat stat_buf; time_t pre_time, post_time; - utbuf.modtime = tst_get_fs_timestamp() - 5; + utbuf.modtime = tst_fs_timestamp_start() - 5; utbuf.actime = utbuf.modtime + 1; TST_EXP_PASS_SILENT(utime(TEMP_FILE, &utbuf)); SAFE_STAT(TEMP_FILE, &stat_buf); @@ -41,11 +39,11 @@ static void run(void) TST_EXP_EQ_LI(stat_buf.st_atime, utbuf.actime); TST_EXP_EQ_LI(stat_buf.st_mtime, utbuf.modtime); - pre_time = tst_get_fs_timestamp(); + pre_time = tst_fs_timestamp_start(); TST_EXP_PASS(utime(TEMP_FILE, NULL), "utime(%s, NULL)", TEMP_FILE); if (!TST_PASS) return; - post_time = tst_get_fs_timestamp(); + post_time = tst_fs_timestamp_end(); SAFE_STAT(TEMP_FILE, &stat_buf); if (stat_buf.st_mtime < pre_time || stat_buf.st_mtime > post_time) diff --git a/testcases/kernel/syscalls/utime/utime02.c b/testcases/kernel/syscalls/utime/utime02.c index fdcd40f6..5ed83298 100755 --- a/testcases/kernel/syscalls/utime/utime02.c +++ b/testcases/kernel/syscalls/utime/utime02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that the system call utime() successfully changes the last * access and modification times of a file to the current time, * under the following constraints: @@ -50,7 +48,7 @@ static void run(void) struct stat stat_buf; time_t pre_time, post_time; - utbuf.modtime = tst_get_fs_timestamp() - 5; + utbuf.modtime = tst_fs_timestamp_start() - 5; utbuf.actime = utbuf.modtime + 1; TST_EXP_PASS_SILENT(utime(TEMP_FILE, &utbuf)); SAFE_STAT(TEMP_FILE, &stat_buf); @@ -58,11 +56,11 @@ static void run(void) TST_EXP_EQ_LI(stat_buf.st_atime, utbuf.actime); TST_EXP_EQ_LI(stat_buf.st_mtime, utbuf.modtime); - pre_time = tst_get_fs_timestamp(); + pre_time = tst_fs_timestamp_start(); TST_EXP_PASS(utime(TEMP_FILE, NULL), "utime(%s, NULL)", TEMP_FILE); if (!TST_PASS) return; - post_time = tst_get_fs_timestamp(); + post_time = tst_fs_timestamp_end(); SAFE_STAT(TEMP_FILE, &stat_buf); if (stat_buf.st_mtime < pre_time || stat_buf.st_mtime > post_time) diff --git a/testcases/kernel/syscalls/utime/utime03.c b/testcases/kernel/syscalls/utime/utime03.c index 734f4897..aa0dcef2 100755 --- a/testcases/kernel/syscalls/utime/utime03.c +++ b/testcases/kernel/syscalls/utime/utime03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that the system call utime() successfully sets the modification * and access times of a file to the current time, under the following * constraints: @@ -70,9 +68,9 @@ static void run(void) } SAFE_SETEUID(user_uid); - mintime = tst_get_fs_timestamp(); + mintime = tst_fs_timestamp_start(); TST_EXP_PASS(utime(TEMP_FILE, NULL)); - maxtime = tst_get_fs_timestamp(); + maxtime = tst_fs_timestamp_end(); SAFE_SETEUID(root_uid); SAFE_STAT(TEMP_FILE, &statbuf); diff --git a/testcases/kernel/syscalls/utime/utime04.c b/testcases/kernel/syscalls/utime/utime04.c index 7b820ab0..0728454b 100755 --- a/testcases/kernel/syscalls/utime/utime04.c +++ b/testcases/kernel/syscalls/utime/utime04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that the system call utime() successfully changes the last * access and modification times of a file to the values specified by * times argument, under the following constraints: diff --git a/testcases/kernel/syscalls/utime/utime05.c b/testcases/kernel/syscalls/utime/utime05.c index 941a3cce..045e275b 100755 --- a/testcases/kernel/syscalls/utime/utime05.c +++ b/testcases/kernel/syscalls/utime/utime05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that the system call utime() successfully changes the last * access and modification times of a file to the values specified by * times argument, under the following constraints: diff --git a/testcases/kernel/syscalls/utime/utime06.c b/testcases/kernel/syscalls/utime/utime06.c index 3ba62a31..c03b1d85 100755 --- a/testcases/kernel/syscalls/utime/utime06.c +++ b/testcases/kernel/syscalls/utime/utime06.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Verify that system call utime() fails with * * - EACCES when times argument is NULL and user does not have rights to modify diff --git a/testcases/kernel/syscalls/utime/utime07.c b/testcases/kernel/syscalls/utime/utime07.c new file mode 100644 index 00000000..e44b246d --- /dev/null +++ b/testcases/kernel/syscalls/utime/utime07.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * Authors: David Fenner, Jon Hendrickson + * Copyright (C) 2024 Andrea Cervesato + */ + +/*\ + * This test verifies that utime() is working correctly on symlink() + * generated files. + * + * Also verify if utime() fails with: + * + * - ENOENT when symlink points to nowhere + * - ELOOP when symlink is looping + */ + +#include +#include "tst_test.h" + +#define TIME_DIFF 100 + +static void create_symlink(const char *path, const char *symname) +{ + struct stat asymlink; + + SAFE_SYMLINK(path, symname); + SAFE_LSTAT(symname, &asymlink); + + if ((asymlink.st_mode & S_IFMT) != S_IFLNK) { + tst_brk(TBROK, "symlink generated a non-symbolic link %s to %s", + symname, path); + } +} + +static void test_utime(void) +{ + char *symname = "my_symlink0"; + struct stat oldsym_stat; + struct stat newsym_stat; + + tst_res(TINFO, "Test if utime() changes access time"); + + create_symlink(tst_tmpdir_path(), symname); + SAFE_STAT(symname, &oldsym_stat); + + struct utimbuf utimes = { + .actime = oldsym_stat.st_atime + TIME_DIFF, + .modtime = oldsym_stat.st_mtime + TIME_DIFF + }; + + TST_EXP_PASS(utime(symname, &utimes)); + SAFE_STAT(symname, &newsym_stat); + + TST_EXP_EQ_LI(newsym_stat.st_atime - oldsym_stat.st_atime, TIME_DIFF); + TST_EXP_EQ_LI(newsym_stat.st_mtime - oldsym_stat.st_mtime, TIME_DIFF); + + SAFE_UNLINK(symname); +} + +static void test_utime_no_path(void) +{ + char *symname = "my_symlink1"; + struct utimbuf utimes; + + tst_res(TINFO, "Test if utime() raises ENOENT when symlink points to nowhere"); + + create_symlink("bc+eFhi!k", symname); + TST_EXP_FAIL(utime(symname, &utimes), ENOENT); + + SAFE_UNLINK(symname); +} + +static void test_utime_loop(void) +{ + char *symname = "my_symlink2"; + struct utimbuf utimes; + + tst_res(TINFO, "Test if utime() raises ELOOP when symlink is looping"); + + create_symlink(symname, symname); + TST_EXP_FAIL(utime(symname, &utimes), ELOOP); + + SAFE_UNLINK(symname); +} + +static void run(void) +{ + test_utime(); + test_utime_no_path(); + test_utime_loop(); +} + +static struct tst_test test = { + .test_all = run, + .needs_tmpdir = 1, +}; diff --git a/testcases/kernel/syscalls/utimensat/utimensat01.c b/testcases/kernel/syscalls/utimensat/utimensat01.c index efcb5c7a..c1132796 100755 --- a/testcases/kernel/syscalls/utimensat/utimensat01.c +++ b/testcases/kernel/syscalls/utimensat/utimensat01.c @@ -3,7 +3,11 @@ * Copyright (c) 2008 Michael Kerrisk * Copyright (c) 2008 Subrata Modak * Copyright (c) 2020 Viresh Kumar + * Copyright (c) Linux Test Project, 2020-2024 * + */ + +/*\ * Basic utimnsat() test. */ diff --git a/testcases/kernel/syscalls/utimes/utimes01.c b/testcases/kernel/syscalls/utimes/utimes01.c index 2bfc1654..a9aa7890 100755 --- a/testcases/kernel/syscalls/utimes/utimes01.c +++ b/testcases/kernel/syscalls/utimes/utimes01.c @@ -1,25 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) Crackerjack Project., 2007 ,Hitachi, Ltd + * Copyright (c) Crackerjack Project., 2007, Hitachi, Ltd * Author(s): Takahiro Yasui + * Copyright (c) Linux Test Project, 2014-2018 * Ported to LTP: Manas Kumar Nayak maknayak@in.ibm.com> */ -/* - * Description: - * Verify that, - * 1) utimes() returns -1 and sets errno to EACCES if times - * is NULL, the caller's effective user ID does not match - * the owner of the file, the caller does not have write - * access to the file, and the caller is not privileged. - * 2) utimes() returns -1 and sets errno to ENOENT if filename - * does not exist. - * 3) utimes() returns -1 and sets errno to EFAULT if filename - * is NULL. - * 4) utimes() returns -1 and sets errno to EPERM if times is - * not NULL, the caller's effective UID does not match the - * owner of the file, and the caller is not privileged. - * 5) utimes() returns -1 and sets errno to EROFS if path resides - * on a read-only file system. + +/*\ + * Verify that, utimes(2) returns -1 and sets errno to + * + * - EACCES if times is NULL, the caller's effective user ID does not match + * the owner of the file, the caller does not have write access to the file, + * and the caller is not privileged + * - ENOENT if filename does not exist + * - EFAULT if filename is NULL + * - EPERM if times is not NULL, the caller's effective UID does not match + * the owner of the file, and the caller is not privileged + * - EROFS if path resides on a read-only file system */ #include diff --git a/testcases/kernel/syscalls/vmsplice/vmsplice02.c b/testcases/kernel/syscalls/vmsplice/vmsplice02.c index 8f1965c2..94c98fb1 100755 --- a/testcases/kernel/syscalls/vmsplice/vmsplice02.c +++ b/testcases/kernel/syscalls/vmsplice/vmsplice02.c @@ -3,15 +3,13 @@ * Copyright (c) 2014 Fujitsu Ltd. * Author: Xing Gu */ -/* - * Description: - * Verify that, - * 1) vmsplice() returns -1 and sets errno to EBADF if fd - * is not valid. - * 2) vmsplice() returns -1 and sets errno to EBADF if fd - * doesn't refer to a pipe. - * 3) vmsplice() returns -1 and sets errno to EINVAL if - * nr_segs is greater than IOV_MAX. + +/*\ + * Verify that, vmsplice(2) returns -1 and sets errno to: + * + * - EBADF if fd is not valid. + * - EBADF if fd doesn't refer to a pipe. + * - EINVAL if nr_segs is greater than IOV_MAX. */ #define _GNU_SOURCE diff --git a/testcases/kernel/syscalls/wait/wait01.c b/testcases/kernel/syscalls/wait/wait01.c index aec91b91..685da14b 100755 --- a/testcases/kernel/syscalls/wait/wait01.c +++ b/testcases/kernel/syscalls/wait/wait01.c @@ -4,8 +4,6 @@ * Author: Zeng Linggang */ /*\ - * [Description] - * * Check that if the calling process does not have any unwaited-for children * wait() will return ECHILD. */ @@ -16,7 +14,7 @@ static void verify_wait(void) { - TST_EXP_FAIL(wait(NULL), ECHILD); + TST_EXP_FAIL2(wait(NULL), ECHILD); } static struct tst_test test = { diff --git a/testcases/kernel/syscalls/wait/wait02.c b/testcases/kernel/syscalls/wait/wait02.c index bc617f7a..d5d721f6 100755 --- a/testcases/kernel/syscalls/wait/wait02.c +++ b/testcases/kernel/syscalls/wait/wait02.c @@ -1,12 +1,10 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * AUTHOR : William Roske * CO-PILOT : Dave Fenner */ /*\ - * [Description] - * * For a terminated child, test whether wait(2) can get its pid * and exit status correctly. */ diff --git a/testcases/kernel/syscalls/wait4/wait402.c b/testcases/kernel/syscalls/wait4/wait402.c index 39b17025..18ed0814 100755 --- a/testcases/kernel/syscalls/wait4/wait402.c +++ b/testcases/kernel/syscalls/wait4/wait402.c @@ -1,101 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * Copyright (c) 2012 Cyril Hrubis - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) 2012 Cyril Hrubis + * Copyright (c) Linux Test Project, 2001-2015 + * Copyright (c) 2023 Ioannis Bonatakis */ - /* - * wait402 - check for ECHILD errno when using an illegal pid value - */ +/*\ + * Check for ECHILD errno when call wait4(2) with an invalid pid value. + */ -#include "test.h" - -#include -#define _USE_BSD -#include -#include +#include "tst_test.h" #include -#include -#include -char *TCID = "wait402"; -int TST_TOTAL = 1; +static pid_t pid_max; -static void cleanup(void); -static void setup(void); - -static long get_pid_max(void) +static void run(void) { - long pid_max; - - SAFE_FILE_SCANF(NULL, "/proc/sys/kernel/pid_max", "%ld", &pid_max); - - return pid_max; -} - -int main(int ac, char **av) -{ - int lc; - pid_t epid = get_pid_max() + 1; - - int status = 1; + int status; struct rusage rusage; - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - TEST(wait4(epid, &status, 0, &rusage)); - - if (TEST_RETURN == 0) { - tst_brkm(TFAIL, cleanup, - "call failed to produce expected error - errno = %d - %s", - TEST_ERRNO, strerror(TEST_ERRNO)); - } - - switch (TEST_ERRNO) { - case ECHILD: - tst_resm(TPASS, - "received expected failure - errno = %d - %s", - TEST_ERRNO, strerror(TEST_ERRNO)); - break; - default: - tst_brkm(TFAIL, cleanup, - "call failed to produce expected " - "error - errno = %d - %s", TEST_ERRNO, - strerror(TEST_ERRNO)); - } - } - - cleanup(); - tst_exit(); + TST_EXP_FAIL2(wait4(pid_max + 1, &status, 0, &rusage), ECHILD); } static void setup(void) { - - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; + SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%d\n", &pid_max); } -static void cleanup(void) -{ -} +static struct tst_test test = { + .test_all = run, + .setup = setup, +}; diff --git a/testcases/kernel/syscalls/wait4/wait403.c b/testcases/kernel/syscalls/wait4/wait403.c index 8746794e..c610a9fa 100755 --- a/testcases/kernel/syscalls/wait4/wait403.c +++ b/testcases/kernel/syscalls/wait4/wait403.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Check wait4(INT_MIN, ...) is not allowed. The pid is negated before * searching for a group with that pid. Negating INT_MIN is not * defined so UBSAN will be triggered if enabled. Also see kill13. diff --git a/testcases/kernel/syscalls/waitid/waitid01.c b/testcases/kernel/syscalls/waitid/waitid01.c index 136eec8a..cb444d19 100755 --- a/testcases/kernel/syscalls/waitid/waitid01.c +++ b/testcases/kernel/syscalls/waitid/waitid01.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This test is checking if waitid() syscall does wait for WEXITED and check for * the return value. */ diff --git a/testcases/kernel/syscalls/waitid/waitid02.c b/testcases/kernel/syscalls/waitid/waitid02.c index f13a4ed0..ea0d2612 100755 --- a/testcases/kernel/syscalls/waitid/waitid02.c +++ b/testcases/kernel/syscalls/waitid/waitid02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests if waitid() returns EINVAL when passed invalid options flag value. */ diff --git a/testcases/kernel/syscalls/waitid/waitid03.c b/testcases/kernel/syscalls/waitid/waitid03.c index ef3fd737..4e70c401 100644 --- a/testcases/kernel/syscalls/waitid/waitid03.c +++ b/testcases/kernel/syscalls/waitid/waitid03.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests if waitid() syscall returns ECHILD when the calling process has no * child processes. */ diff --git a/testcases/kernel/syscalls/waitid/waitid04.c b/testcases/kernel/syscalls/waitid/waitid04.c index 96c1cf8b..0d4b1611 100644 --- a/testcases/kernel/syscalls/waitid/waitid04.c +++ b/testcases/kernel/syscalls/waitid/waitid04.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * This test if waitid() syscall leaves the si_pid set to 0 with WNOHANG flag * when no child was waited for. */ diff --git a/testcases/kernel/syscalls/waitid/waitid05.c b/testcases/kernel/syscalls/waitid/waitid05.c index 1b9186dc..db9d3b22 100644 --- a/testcases/kernel/syscalls/waitid/waitid05.c +++ b/testcases/kernel/syscalls/waitid/waitid05.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests if waitid() filters children correctly by the group ID. * * - waitid() with GID + 1 returns ECHILD diff --git a/testcases/kernel/syscalls/waitid/waitid06.c b/testcases/kernel/syscalls/waitid/waitid06.c index 5f51c81c..ed6404bf 100644 --- a/testcases/kernel/syscalls/waitid/waitid06.c +++ b/testcases/kernel/syscalls/waitid/waitid06.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests if waitid() filters children correctly by the PID. * * - waitid() with PID + 1 returns ECHILD diff --git a/testcases/kernel/syscalls/waitid/waitid07.c b/testcases/kernel/syscalls/waitid/waitid07.c index d607dbd8..e5df1cc4 100644 --- a/testcases/kernel/syscalls/waitid/waitid07.c +++ b/testcases/kernel/syscalls/waitid/waitid07.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test if waitid() filters children killed with SIGSTOP. */ diff --git a/testcases/kernel/syscalls/waitid/waitid08.c b/testcases/kernel/syscalls/waitid/waitid08.c index 2da680e6..519ccf78 100644 --- a/testcases/kernel/syscalls/waitid/waitid08.c +++ b/testcases/kernel/syscalls/waitid/waitid08.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test if waitid() filters children killed with SIGCONT. */ diff --git a/testcases/kernel/syscalls/waitid/waitid09.c b/testcases/kernel/syscalls/waitid/waitid09.c index 115c2e67..3a3080bc 100644 --- a/testcases/kernel/syscalls/waitid/waitid09.c +++ b/testcases/kernel/syscalls/waitid/waitid09.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Test that waitid() fails with ECHILD with process that is not child of the * current process. We fork() one child just to be sure that there are unwaited * for children available while the test runs. diff --git a/testcases/kernel/syscalls/waitid/waitid10.c b/testcases/kernel/syscalls/waitid/waitid10.c index e55e88c2..14d4ba76 100644 --- a/testcases/kernel/syscalls/waitid/waitid10.c +++ b/testcases/kernel/syscalls/waitid/waitid10.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This test is checking if waitid() syscall recognizes a process that ended * with division by zero error. */ @@ -76,4 +74,5 @@ static struct tst_test test = { {&infop, .size = sizeof(*infop)}, {}, }, + .needs_tmpdir = 1, }; diff --git a/testcases/kernel/syscalls/waitid/waitid11.c b/testcases/kernel/syscalls/waitid/waitid11.c index e3754bb1..63d011e6 100644 --- a/testcases/kernel/syscalls/waitid/waitid11.c +++ b/testcases/kernel/syscalls/waitid/waitid11.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * This test is checking if waitid() syscall recognizes a process that has been * killed with SIGKILL. */ diff --git a/testcases/kernel/syscalls/waitpid/.gitignore b/testcases/kernel/syscalls/waitpid/.gitignore index cf9ddf6f..51a42465 100755 --- a/testcases/kernel/syscalls/waitpid/.gitignore +++ b/testcases/kernel/syscalls/waitpid/.gitignore @@ -1,8 +1,6 @@ /waitpid01 -/waitpid02 /waitpid03 /waitpid04 -/waitpid05 /waitpid06 /waitpid07 /waitpid08 diff --git a/testcases/kernel/syscalls/waitpid/waitpid01.c b/testcases/kernel/syscalls/waitpid/waitpid01.c index 5a39a110..cb65d1e2 100755 --- a/testcases/kernel/syscalls/waitpid/waitpid01.c +++ b/testcases/kernel/syscalls/waitpid/waitpid01.c @@ -5,27 +5,99 @@ * Copyright (c) 2018 Cyril Hrubis */ -/* - * Check that when a child kills itself by SIGALRM the waiting parent is - * correctly notified. +/*\ + * Check that when a child kills itself with one of the standard signals, + * the waiting parent is correctly notified. * - * Fork a child that raises(SIGALRM), the parent checks that SIGALRM was - * returned. + * Fork a child that sends given signal to itself using raise() or kill(), + * the parent checks that the signal was returned. */ #include #include #include "tst_test.h" -static void run(void) +static int test_coredump; +static struct testcase { + int sig; + int coredump; +} testcase_list[] = { + {SIGABRT, 1}, + {SIGALRM, 0}, + {SIGBUS, 1}, + {SIGFPE, 1}, + {SIGHUP, 0}, + {SIGILL, 1}, + {SIGINT, 0}, + {SIGKILL, 0}, + {SIGPIPE, 0}, + {SIGPOLL, 0}, + {SIGPROF, 0}, + {SIGQUIT, 1}, + {SIGSEGV, 1}, + {SIGSYS, 1}, + {SIGTERM, 0}, + {SIGTRAP, 1}, + {SIGUSR1, 0}, + {SIGUSR2, 0}, + {SIGVTALRM, 0}, + {SIGXCPU, 1}, + {SIGXFSZ, 1} +}; + +static void child_raise(int sig) +{ + raise(sig); + exit(0); +} + +static void child_kill(int sig) +{ + kill(getpid(), sig); + exit(0); +} + +static struct testvariant { + void (*func)(int sig); + const char *desc; +} variant_list[] = { + {child_raise, "raise(sig)"}, + {child_kill, "kill(getpid(), sig)"} +}; + +static void setup(void) +{ + struct rlimit lim = { 0 }; + + /* Disable core dumps */ + SAFE_GETRLIMIT(RLIMIT_CORE, &lim); + + if (lim.rlim_max) { + lim.rlim_cur = getpagesize(); + + if (lim.rlim_max > 0 && lim.rlim_max < lim.rlim_cur) + lim.rlim_cur = lim.rlim_max; + + SAFE_SETRLIMIT(RLIMIT_CORE, &lim); + test_coredump = 1; + } else { + tst_res(TCONF, "Skipping coredump tests due to low rlimit"); + } + + tst_res(TINFO, "Testing child: %s", variant_list[tst_variant].desc); +} + +static void run(unsigned int n) { pid_t pid; int status; + const struct testcase *tc = testcase_list + n; + + if (tc->sig != SIGKILL) + SAFE_SIGNAL(tc->sig, SIG_DFL); pid = SAFE_FORK(); - if (!pid) { - raise(SIGALRM); - exit(0); - } + if (!pid) + variant_list[tst_variant].func(tc->sig); TST_EXP_PID_SILENT(waitpid(pid, &status, 0)); if (!TST_PASS) @@ -40,22 +112,43 @@ static void run(void) if (!WIFSIGNALED(status)) { tst_res(TFAIL, "WIFSIGNALED() not set in status (%s)", - tst_strstatus(status)); + tst_strstatus(status)); return; } tst_res(TPASS, "WIFSIGNALED() set in status"); - if (WTERMSIG(status) != SIGALRM) { - tst_res(TFAIL, "WTERMSIG() != SIGALRM but %s", - tst_strsig(WTERMSIG(status))); + if (WTERMSIG(status) != tc->sig) { + tst_res(TFAIL, "WTERMSIG() != %s but %s", tst_strsig(tc->sig), + tst_strsig(WTERMSIG(status))); return; } - tst_res(TPASS, "WTERMSIG() == SIGALRM"); + tst_res(TPASS, "WTERMSIG() == %s", tst_strsig(tc->sig)); + + if (!test_coredump) + return; + + if (!tc->coredump) { + if (WCOREDUMP(status)) + tst_res(TFAIL, "Child unexpectedly dumped core"); + + return; + } + + if (!WCOREDUMP(status)) { + tst_res(TFAIL, "Child did not dump core when expected"); + return; + } + + tst_res(TPASS, "Child dumped core as expected"); } static struct tst_test test = { .forks_child = 1, - .test_all = run, + .setup = setup, + .test = run, + .tcnt = ARRAY_SIZE(testcase_list), + .test_variants = ARRAY_SIZE(variant_list), + .needs_tmpdir = 1 /* for coredumps */ }; diff --git a/testcases/kernel/syscalls/waitpid/waitpid02.c b/testcases/kernel/syscalls/waitpid/waitpid02.c deleted file mode 100755 index 1164a783..00000000 --- a/testcases/kernel/syscalls/waitpid/waitpid02.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ - -/* - * NAME - * waitpid02.c - * - * DESCRIPTION - * Check that when a child gets killed by an integer zero - * divide exception, the waiting parent is correctly notified. - * - * ALGORITHM - * Fork a child and send a SIGFPE to it. The parent waits for the - * death of the child and checks that SIGFPE was returned. - * - * USAGE: - * waitpid02 [-c n] [-e] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 07/2001 John George - * -Ported - * 10/2002 Paul Larson - * Div by zero doesn't cause SIGFPE on some archs, fixed - * to send the signal with kill - * - * Restrictions - * None - */ - -#include -#include -#include -#include -#include -#include -#include "test.h" - -static void do_child(void); -static void setup(void); - -char *TCID = "waitpid02"; -int TST_TOTAL = 1; - -int main(int argc, char **argv) -{ - int lc; - - int pid, npid, sig, nsig; - int nexno, status; - - tst_parse_opts(argc, argv, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, ""); -#endif - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - sig = SIGFPE; - - pid = FORK_OR_VFORK(); - - if (pid < 0) - tst_brkm(TBROK|TERRNO, NULL, "fork failed"); - - if (pid == 0) { -#ifdef UCLINUX - self_exec(argv[0], ""); - /* No fork() error check is done so don't check here */ -#else - do_child(); -#endif - } else { - kill(pid, sig); - errno = 0; - while (((npid = waitpid(pid, &status, 0)) != -1) || - (errno == EINTR)) { - if (errno == EINTR) - continue; - - if (npid != pid) { - tst_resm(TFAIL, "waitpid error: " - "unexpected pid returned"); - } else { - tst_resm(TPASS, - "received expected pid"); - } - - nsig = WTERMSIG(status); - - /* - * nsig is the signal number returned by - * waitpid - */ - if (nsig != sig) { - tst_resm(TFAIL, "waitpid error: " - "unexpected signal returned"); - } else { - tst_resm(TPASS, "received expected " - "signal"); - } - - /* - * nexno is the exit number returned by - * waitpid - */ - nexno = WEXITSTATUS(status); - if (nexno != 0) { - tst_resm(TFAIL, "signal error: " - "unexpected exit number " - "returned"); - } else { - tst_resm(TPASS, "received expected " - "exit value"); - } - } - } - } - - tst_exit(); -} - -static void do_child(void) -{ - int exno = 1; - - while (1) - usleep(10); - - exit(exno); -} - -static void setup(void) -{ - /* SIGFPE is expected signal, so avoid creating any corefile. - * '1' is a special value, that will also avoid dumping via pipe. */ - struct rlimit r; - r.rlim_cur = 1; - r.rlim_max = 1; - setrlimit(RLIMIT_CORE, &r); - - TEST_PAUSE; -} diff --git a/testcases/kernel/syscalls/waitpid/waitpid03.c b/testcases/kernel/syscalls/waitpid/waitpid03.c index 7eae3ad5..12a2a214 100755 --- a/testcases/kernel/syscalls/waitpid/waitpid03.c +++ b/testcases/kernel/syscalls/waitpid/waitpid03.c @@ -1,194 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) 2024 SUSE LLC */ -/* - * NAME - * waitpid03.c - * - * DESCRIPTION - * Check that parent waits unitl specific child has returned. - * - * ALGORITHM - * Parent forks numerous (22 = MAXUPRC - 3) children, and starts waits : - * Should only wait for the specific child, a second wait on the same - * child should return with -1 and not one of the other zombied - * children. - * - * USAGE: - * waitpid03 [-c n] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 07/2001 John George - * -Ported - * 04/2002 wjhuie sigset cleanups - * - * Restrictions - * None +/*\ + * Check that waitpid() returns the exit status of a specific child process + * and repeated call on the same process will fail with ECHILD. */ -#define DEBUG 0 - +#include #include #include #include #include -#include "test.h" -static void do_child(int); -static void setup(void); -static void cleanup(void); +#include "tst_test.h" -char *TCID = "waitpid03"; -int TST_TOTAL = 1; +#define MAX_CHILDREN 25 -#define MAXUPRC 25 +static pid_t children[MAX_CHILDREN]; -static int ikids; -static int pid[MAXUPRC]; -static int condition_number; - -#ifdef UCLINUX -static void do_child_uclinux(void); -static int ikids_uclinux; -#endif - -int main(int argc, char **argv) +static void check_waitpid(pid_t pid, int reaped) { - int lc; + TEST(waitpid(pid, NULL, 0)); - int status, ret; - - tst_parse_opts(argc, argv, NULL, NULL); -#ifdef UCLINUX - maybe_run_child(&do_child, "d", &ikids_uclinux); -#endif - - setup(); - - /* check for looping state if -i option is given */ - for (lc = 0; TEST_LOOPING(lc); lc++) { - /* reset tst_count in case we are looping */ - tst_count = 0; - - /* - * Set SIGTERM to SIG_DFL as test driver sets up to ignore - * SIGTERM - */ - if ((sig_t) signal(SIGTERM, SIG_DFL) == SIG_ERR) { - tst_resm(TFAIL, "Signal SIGTERM failed, errno = %d", - errno); - - } - - while (++ikids < MAXUPRC) { - pid[ikids] = FORK_OR_VFORK(); - if (pid[ikids] > 0) { - if (DEBUG) - tst_resm(TINFO, "child # %d", ikids); - } else if (pid[ikids] == -1) { - tst_brkm(TBROK|TERRNO, cleanup, "cannot open " - "fork #%d", ikids); - - } else { -#ifdef UCLINUX - if (self_exec(argv[0], "d", ikids) < 0) { - tst_resm(TFAIL, "cannot self_exec #%d", - ikids); - } -#else - do_child(ikids); -#endif - } - } - - for (ikids = 1; ikids < MAXUPRC; ikids++) { - if (DEBUG) - tst_resm(TINFO, "Killing #%d", ikids); - kill(pid[ikids], SIGTERM); - } - - ikids = 0; - condition_number = 1; - - /* Wait on one specific child */ - if (DEBUG) - tst_resm(TINFO, "Waiting for child:#%d", MAXUPRC / 2); - ret = waitpid(pid[MAXUPRC / 2], &status, 0); - if (ret != pid[MAXUPRC / 2]) { - tst_resm(TFAIL, "condition %d test failed. " - "waitpid(%d) returned %d.", - condition_number, pid[MAXUPRC / 2], ret); - } else { - tst_resm(TPASS, "Got correct child PID"); - } - condition_number++; - - /* - * Child has already been waited on, waitpid should return - * -1 - */ - ret = waitpid(pid[MAXUPRC / 2], &status, 0); - if (ret != -1) { - tst_resm(TFAIL, "condition %d test failed", - condition_number); - } else { - tst_resm(TPASS, "Condition %d test passed", - condition_number); - } - condition_number++; + if (!reaped && pid == (pid_t)TST_RET) { + tst_res(TPASS, "waitpid(%d) returned correct PID", pid); + return; } - cleanup(); - tst_exit(); + if (reaped && TST_RET == -1 && TST_ERR == ECHILD) { + tst_res(TPASS | TTERRNO, "waitpid(%d) failed on reaped child", + pid); + return; + } + + if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, "waitpid(%d) failed", pid); + return; + } + + if (TST_RET < 0) { + tst_res(TFAIL | TTERRNO, + "Unexpected waitpid(%d) return value %ld", pid, + TST_RET); + return; + } + + tst_res(TFAIL, "waitpid(%d) returned unexpected PID %ld", pid, TST_RET); } -static void do_child(int ikids) +static void run(void) { - if (DEBUG) - tst_resm(TINFO, "child:%d", ikids); - pause(); - exit(0); + int i; + + for (i = 0; i < MAX_CHILDREN; i++) { + children[i] = SAFE_FORK(); + + /* Children have nothing to do... */ + if (!children[i]) + exit(0); + } + + /* Wait for one specific child */ + i = MAX_CHILDREN / 2; + check_waitpid(children[i], 0); + + /* Try the same child again after it was reaped */ + check_waitpid(children[i], 1); + tst_reap_children(); } -#ifdef UCLINUX -/* - * do_child_uclinux() - * run do_child with the appropriate ikids variable - */ -static void do_child_uclinux(void) -{ - do_child(ikids_uclinux); -} -#endif - -static void setup(void) -{ - TEST_PAUSE; -} - -static void cleanup(void) -{ - while (ikids-- > 1) - kill(pid[ikids], SIGKILL); -} +static struct tst_test test = { + .test_all = run, + .forks_child = 1 +}; diff --git a/testcases/kernel/syscalls/waitpid/waitpid04.c b/testcases/kernel/syscalls/waitpid/waitpid04.c index abf94eed..ee8e8daa 100755 --- a/testcases/kernel/syscalls/waitpid/waitpid04.c +++ b/testcases/kernel/syscalls/waitpid/waitpid04.c @@ -1,144 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 + * Copyright (c) International Business Machines Corp., 2001 + * Copyright (c) 2024 SUSE LLC */ -/* - * NAME - * waitpid04.c - * - * DESCRIPTION - * test to check the error conditions in waitpid sys call - * - * USAGE: - * waitpid04 [-c n] [-e] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 07/2001 John George - * -Ported - * - * Restrictions - * NONE +/*\ + * Test to check the error conditions in waitpid() syscall. */ -#include -#include +#include #include -#include -#include "test.h" +#include "tst_test.h" -static void setup(void); -static void cleanup(void); +static struct testcase { + pid_t pid; + int flags; + int err; +} testcase_list[] = { + {-1, 0, ECHILD}, /* Wait for any child when none exist */ + {1, 0, ECHILD}, /* Wait for non-child process */ + {-1, -1, EINVAL}, /* Invalid flags */ + {INT_MIN, 0, ESRCH}, /* Wait for invalid process group */ +}; -char *TCID = "waitpid04"; -int TST_TOTAL = 1; - -#define INVAL_FLAG -1 - -static int flag, condition_number; - -int main(int ac, char **av) +static void run(unsigned int n) { - int pid, status, ret; + const struct testcase *tc = testcase_list + n; - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - - setup(); - - /* check for looping state if -i option is given */ - for (lc = 0; TEST_LOOPING(lc); lc++) { - /* reset tst_count in case we are looping */ - tst_count = 0; - - ret = waitpid(pid, &status, WNOHANG); - flag = 0; - condition_number = 1; - if (ret != -1) { - tst_resm(TFAIL, "condition %d test failed", - condition_number); - } else { - if (errno != ECHILD) { - tst_resm(TFAIL, "waitpid() set invalid " - "errno, expected ECHILD, got: %d", - errno); - } else { - tst_resm(TPASS, "condition %d test passed", - condition_number); - } - } - condition_number++; - - if (FORK_OR_VFORK() == 0) - exit(0); - - pid = 1; - ret = waitpid(pid, &status, WUNTRACED); - flag = 0; - if (ret != -1) { - tst_resm(TFAIL, "condition %d test failed", - condition_number); - } else { - if (errno != ECHILD) { - tst_resm(TFAIL, "waitpid() set invalid " - "errno, expected ECHILD, got: %d", - errno); - } else { - tst_resm(TPASS, "condition %d test passed", - condition_number); - } - } - condition_number++; - - /* Option is Inval = INVAL_FLAG */ - ret = waitpid(pid, &status, INVAL_FLAG); - flag = 0; - if (ret != -1) { - tst_resm(TFAIL, "condition %d test failed", - condition_number); - } else { - if (errno != EINVAL) { - tst_resm(TFAIL, "waitpid() set invalid " - "errno, expected EINVAL, got: %d", - errno); - } else { - tst_resm(TPASS, "condition %d test passed", - condition_number); - } - } - condition_number++; - } - - cleanup(); - tst_exit(); + TST_EXP_FAIL2(waitpid(tc->pid, NULL, tc->flags), tc->err, + "waipid(%d, NULL, 0x%x)", tc->pid, tc->flags); } -static void setup(void) -{ - TEST_PAUSE; -} - -static void cleanup(void) -{ -} +static struct tst_test test = { + .test = run, + .tcnt = ARRAY_SIZE(testcase_list) +}; diff --git a/testcases/kernel/syscalls/waitpid/waitpid05.c b/testcases/kernel/syscalls/waitpid/waitpid05.c deleted file mode 100755 index 5466fd91..00000000 --- a/testcases/kernel/syscalls/waitpid/waitpid05.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * - * Copyright (c) International Business Machines Corp., 2001 - * - * 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 - */ - -/* - * NAME - * waitpid05.c - * - * DESCRIPTION - * Check that when a child kills itself with a kill statement after - * determining its process id by using getpid, the parent receives a - * correct report of the cause of its death. This also indirectly - * checks that getpid returns the correct process id. - * - * ALGORITHM - * For signals 1 - 15: fork a child that determines it's own process - * id, then sends the signal to itself. The parent waits to see if the - * demise of the child results in the signal number being returned to - * the parent. - * - * USAGE: - * waitpid05 [-c n] [-e] [-i n] [-I x] [-P x] [-t] - * where, -c n : Run n copies concurrently. - * -e : Turn on errno logging. - * -i n : Execute test n times. - * -I x : Execute test for x seconds. - * -P x : Pause for x seconds between iterations. - * -t : Turn on syscall timing. - * - * History - * 07/2001 John George - * -Ported - * 04/2002 wjhuie sigset cleanups - * - * Restrictions - * None - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "test.h" - -static void do_child(int); -static void setup(void); -static void cleanup(void); - -char *TCID = "waitpid05"; -int TST_TOTAL = 1; - -#ifdef UCLINUX -static void do_child_uclinux(void); -static int sig_uclinux; -#endif - -int main(int ac, char **av) -{ - int pid, npid, sig, nsig; - int exno, nexno, status; - int lc; - - tst_parse_opts(ac, av, NULL, NULL); - -#ifdef UCLINUX - maybe_run_child(&do_child_uclinux, "d", &sig_uclinux); -#endif - - setup(); - - /* check for looping state if -i option is given */ - for (lc = 0; TEST_LOOPING(lc); lc++) { - /* reset tst_count in case we are looping */ - tst_count = 0; - - /* - * Set SIGTERM to SIG_DFL as test driver sets up to ignore - * SIGTERM - */ - if (signal(SIGTERM, SIG_DFL) == SIG_ERR) { - tst_resm(TFAIL, "Sigset SIGTERM failed, errno = %d", - errno); - - } - - exno = 1; - for (sig = 1; sig <= 15; sig++) { - if (sig == SIGUSR1 || sig == SIGUSR2 || sig == SIGBUS) - continue; - - /*Initialize signal to its default action */ - signal(sig, SIG_DFL); - pid = FORK_OR_VFORK(); - - if (pid == 0) { -#ifdef UCLINUX - self_exec(av[0], "d", sig); - /* No fork() error check is done so don't */ - /* do an error check here */ -#else - do_child(sig); -#endif - } else { - errno = 0; - while (((npid = waitpid(pid, &status, 0)) != - -1) || (errno == EINTR)) { - if (errno == EINTR) - continue; - - if (npid != pid) { - tst_resm(TFAIL, "waitpid " - "error: unexpected " - "pid returned"); - } else { - tst_resm(TPASS, "received " - "expected pid."); - } - - nsig = status % 256; - - /* - * to check if the core dump bit has - * been set, bit #7 - */ - if (nsig >= 128) { - nsig -= 128; - if ((sig == 1) || (sig == 2) || - (sig == 9) || (sig == 13) || - (sig == 14) || - (sig == 15)) { - tst_resm(TFAIL, - "signal " - "error : " - "core dump " - "bit set for" - " exception " - "number %d", - sig); - } - } else if ((sig == 3) || (sig == 4) || - (sig == 5) || (sig == 6) || - (sig == 8) || (sig == 11)) { - tst_resm(TFAIL, - "signal error: " - "core dump bit not " - "set for exception " - "number %d", sig); - } - - /* - * nsig is the signal number returned - * by waitpid - */ - if (nsig != sig) { - tst_resm(TFAIL, "waitpid " - "error: unexpected " - "signal returned"); - tst_resm(TINFO, "got signal " - "%d, expected " - "%d", nsig, sig); - } - - /* - * nexno is the exit number returned - * by waitpid - */ - nexno = status / 256; - if (nexno != 0) { - tst_resm(TFAIL, "signal " - "error: unexpected " - "exit number " - "returned"); - } else { - tst_resm(TPASS, "received " - "expected exit number."); - } - } - } - } - - if (access("core", F_OK) == 0) - unlink("core"); - } - - cleanup(); - tst_exit(); -} - -static void do_child(int sig) -{ - int exno = 1; - int pid = getpid(); - - if (kill(pid, sig) == -1) { - tst_resm(TFAIL, "kill error: kill unsuccessful"); - exit(exno); - } -} - -#ifdef UCLINUX -/* - * do_child_uclinux() - * run do_child with the appropriate sig variable - */ -static void do_child_uclinux(void) -{ - do_child(sig_uclinux); -} -#endif - -static void setup(void) -{ - struct rlimit newlimit; - - TEST_PAUSE; - - tst_tmpdir(); - - newlimit.rlim_max = newlimit.rlim_cur = RLIM_INFINITY; - if (setrlimit(RLIMIT_CORE, &newlimit) != 0) - tst_resm(TWARN, - "setrlimit(RLIMIT_CORE,RLIM_INFINITY) failed; this may cause some false core-dump test failures"); -} - -static void cleanup(void) -{ - tst_rmdir(); -} diff --git a/testcases/kernel/syscalls/write/write02.c b/testcases/kernel/syscalls/write/write02.c index ab38dce7..a5855c8d 100755 --- a/testcases/kernel/syscalls/write/write02.c +++ b/testcases/kernel/syscalls/write/write02.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Tests for a special case NULL buffer with size 0 is expected to return 0. */ diff --git a/testcases/kernel/syscalls/write/write04.c b/testcases/kernel/syscalls/write/write04.c index a5d62e0f..41bd35a1 100755 --- a/testcases/kernel/syscalls/write/write04.c +++ b/testcases/kernel/syscalls/write/write04.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Verify that write(2) fails with errno EAGAIN when attempt to write to fifo * opened in O_NONBLOCK mode. */ diff --git a/testcases/kernel/syscalls/write/write05.c b/testcases/kernel/syscalls/write/write05.c index b907624a..96988b0d 100755 --- a/testcases/kernel/syscalls/write/write05.c +++ b/testcases/kernel/syscalls/write/write05.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Check the return value, and errnos of write(2) * * - when the file descriptor is invalid - EBADF diff --git a/testcases/kernel/syscalls/write/write06.c b/testcases/kernel/syscalls/write/write06.c index aac1e6f7..068aaa58 100755 --- a/testcases/kernel/syscalls/write/write06.c +++ b/testcases/kernel/syscalls/write/write06.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Test the write() system call with O_APPEND. * * The full description of O_APPEND is in open(2) man-pages: diff --git a/testcases/kernel/syscalls/writev/Makefile b/testcases/kernel/syscalls/writev/Makefile index 6627abae..2eb46974 100755 --- a/testcases/kernel/syscalls/writev/Makefile +++ b/testcases/kernel/syscalls/writev/Makefile @@ -3,10 +3,6 @@ top_srcdir ?= ../../../.. -ifeq ($(UCLINUX),1) -FILTER_OUT_MAKE_TARGETS += writev02 -endif - include $(top_srcdir)/include/mk/testcases.mk writev03: CFLAGS += -pthread diff --git a/testcases/kernel/syscalls/writev/writev02.c b/testcases/kernel/syscalls/writev/writev02.c index 005f37a1..a7d11de3 100755 --- a/testcases/kernel/syscalls/writev/writev02.c +++ b/testcases/kernel/syscalls/writev/writev02.c @@ -179,8 +179,7 @@ void setup(void) strcpy(name, DATA_FILE); sprintf(f_name, "%s.%d", name, getpid()); - bad_addr = mmap(0, 1, PROT_NONE, - MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0); + bad_addr = mmap(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (bad_addr == MAP_FAILED) tst_brkm(TBROK | TERRNO, cleanup, "mmap failed"); wr_iovec[0].iov_base = bad_addr; diff --git a/testcases/kernel/syscalls/writev/writev03.c b/testcases/kernel/syscalls/writev/writev03.c index 3575ca44..7ef20970 100755 --- a/testcases/kernel/syscalls/writev/writev03.c +++ b/testcases/kernel/syscalls/writev/writev03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Check for potential issues in writev() if the first iovec entry is NULL * and the next one is not present in RAM. This can result in a brief window * where writev() first writes uninitialized data into the file (possibly @@ -35,7 +33,7 @@ static unsigned char buf[BUF_SIZE], *map_ptr; static int mapfd = -1, writefd = -1, readfd = -1; -static int written; +static tst_atomic_t written; static struct tst_fzsync_pair fzsync_pair; struct iovec iov[5]; @@ -145,7 +143,8 @@ static struct tst_test test = { .min_cpus = 2, .setup = setup, .cleanup = cleanup, - .max_runtime = 75, + .runtime = 75, + .min_runtime = 16, .tags = (const struct tst_tag[]) { {"linux-git", "d4690f1e1cda"}, {} diff --git a/testcases/kernel/syscalls/writev/writev05.c b/testcases/kernel/syscalls/writev/writev05.c index c939e7de..3bee6f59 100755 --- a/testcases/kernel/syscalls/writev/writev05.c +++ b/testcases/kernel/syscalls/writev/writev05.c @@ -83,8 +83,6 @@ long l_seek(int, long, int); void setup(void); void cleanup(void); -#if !defined(UCLINUX) - int main(int argc, char **argv) { int lc; @@ -178,16 +176,6 @@ int main(int argc, char **argv) } -#else - -int main(void) -{ - tst_resm(TINFO, "test is not available on uClinux"); - tst_exit(); -} - -#endif /* if !defined(UCLINUX) */ - /* * setup() * performs all ONE TIME setup for this test @@ -210,8 +198,7 @@ void setup(void) strcpy(name, DATA_FILE); sprintf(f_name, "%s.%d", name, getpid()); - bad_addr = mmap(0, 1, PROT_NONE, - MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0); + bad_addr = mmap(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (bad_addr == MAP_FAILED) { printf("mmap failed\n"); } diff --git a/testcases/kernel/syscalls/writev/writev06.c b/testcases/kernel/syscalls/writev/writev06.c index e97ae6f2..b60c727b 100755 --- a/testcases/kernel/syscalls/writev/writev06.c +++ b/testcases/kernel/syscalls/writev/writev06.c @@ -162,22 +162,22 @@ void setup(void) /* Crate two readable and writeble mappings with non reabable * mapping around */ bad_addr[0] = mmap(NULL, page_size * 3, PROT_NONE, - MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0); + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (bad_addr[0] == MAP_FAILED) tst_brkm(TBROK, cleanup, "mmap failed for bad_addr[0]"); good_addr[0] = mmap(NULL, page_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0); + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (good_addr[0] == MAP_FAILED) tst_brkm(TBROK, cleanup, "mmap failed for good_addr[0]"); bad_addr[1] = mmap(NULL, page_size * 3, PROT_NONE, - MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0); + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (bad_addr[1] == MAP_FAILED) tst_brkm(TBROK, cleanup, "mmap failed for bad_addr[1]"); good_addr[1] = mmap(NULL, page_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0); + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (good_addr[1] == MAP_FAILED) tst_brkm(TBROK, cleanup, "mmap failed for good_addr[1]"); diff --git a/testcases/kernel/syscalls/writev/writev07.c b/testcases/kernel/syscalls/writev/writev07.c index b725f08d..0798296a 100755 --- a/testcases/kernel/syscalls/writev/writev07.c +++ b/testcases/kernel/syscalls/writev/writev07.c @@ -4,20 +4,24 @@ * Copyright (c) Linux Test Project, 2016 */ -/* - * Test Description: - * Verify writev() behaviour with partially valid iovec list. - * Kernel <4.8 used to shorten write up to first bad invalid - * iovec. Starting with 4.8, a writev with short data (under - * page size) is likely to get shorten to 0 bytes and return - * EFAULT. +/*\ + * Verify writev() behaviour with partially valid iovec list. + * Kernel <4.8 used to shorten write up to first bad invalid + * iovec. Starting with 4.8, a writev with short data (under + * page size) is likely to get shorten to 0 bytes and return + * EFAULT. * - * This test doesn't make assumptions how much will write get - * shortened. It only tests that file content/offset after - * syscall corresponds to return value of writev(). + * This test doesn't make assumptions how much will write get + * shortened. It only tests that file content/offset after + * syscall corresponds to return value of writev(). * - * See: [RFC] writev() semantics with invalid iovec in the middle - * https://marc.info/?l=linux-kernel&m=147388891614289&w=2 + * See: [RFC] writev() semantics with invalid iovec in the middle + * https://marc.info/?l=linux-kernel&m=147388891614289&w=2. + * + * This is also regression test for kernel commits: + * + * * 20c64ec83a9f ("iomap: fix a regression for partial write errors") + * * 3ac974796e5d ("iomap: fix short copy in iomap_write_iter()") */ #include @@ -138,4 +142,9 @@ static struct tst_test test = { .needs_tmpdir = 1, .setup = setup, .test_all = test_writev, + .tags = (const struct tst_tag[]) { + {"linux-git", "20c64ec83a9f"}, + {"linux-git", "3ac974796e5d"}, + {}, + } }; diff --git a/testcases/kernel/tracing/pt_test/pt_test.c b/testcases/kernel/tracing/pt_test/pt_test.c index 54011a88..c202c2e2 100755 --- a/testcases/kernel/tracing/pt_test/pt_test.c +++ b/testcases/kernel/tracing/pt_test/pt_test.c @@ -14,6 +14,7 @@ */ +#define _GNU_SOURCE #include #include #include @@ -35,6 +36,8 @@ #define INTEL_PT_FORMAT_TSC "/sys/devices/intel_pt/format/tsc" #define INTEL_PT_FORMAT_NRT "/sys/devices/intel_pt/format/noretcomp" +#define MSR_IA32_VMX_MISC 0x00000485 + //Intel PT event handle int fde = -1; //map head and size @@ -102,6 +105,36 @@ static void del_map(uint64_t **buf_ev, long bufsize) free(buf_ev); } +/* Some versions of Intel PT do not support tracing across VMXON */ +static int is_supported_across_vmxon(void) +{ + char msr_path[64]; + int msr_fd, ret, supported; + uint64_t value; + + sprintf(msr_path, "/dev/cpu/%d/msr", sched_getcpu()); + + if (access(msr_path, F_OK) != 0) { + tst_res(TINFO, "%s not present", msr_path); + tst_res(TINFO, "skipping check for INTEL PT support across VMXON"); + return 1; + } + + msr_fd = SAFE_OPEN(msr_path, O_RDONLY); + ret = pread(msr_fd, &value, sizeof(value), MSR_IA32_VMX_MISC); + SAFE_CLOSE(msr_fd); + if (ret == sizeof(value)) { + supported = value & (1 << 14); + + tst_res(TINFO, "Intel PT %s supported across VMXON", + supported ? "" : "_NOT_"); + return supported; + } + + /* we failed on MSR read, so assume it's supported */ + return 1; +} + static void intel_pt_trace_check(void) { uint64_t aux_head = 0; @@ -117,6 +150,11 @@ static void intel_pt_trace_check(void) pmp = (struct perf_event_mmap_page *)bufm[0]; aux_head = *(volatile uint64_t *)&pmp->aux_head; if (aux_head == 0) { + if ((access("/sys/module/kvm_intel", F_OK) == 0) + && (!is_supported_across_vmxon())) { + tst_brk(TCONF, "Intel PT can not run at the same time as virtualization"); + } + tst_res(TFAIL, "There is no trace"); return; } diff --git a/testcases/kernel/uevents/Makefile b/testcases/kernel/uevents/Makefile index d5ceb071..12fc57df 100755 --- a/testcases/kernel/uevents/Makefile +++ b/testcases/kernel/uevents/Makefile @@ -2,7 +2,7 @@ top_srcdir ?= ../../.. -LTPLIBS = ltpuinput +LTPLIBS = uinput uevent03: LDLIBS += -lltpuinput diff --git a/testcases/kernel/uevents/uevent01.c b/testcases/kernel/uevents/uevent01.c index 5c65556c..09ef4241 100755 --- a/testcases/kernel/uevents/uevent01.c +++ b/testcases/kernel/uevents/uevent01.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Cyril Hrubis + * Copyright (c) Linux Test Project, 2019-2023 */ -/* +/*\ * Very simple uevent netlink socket test. * * We fork a child that listens for a kernel events while parents attaches and diff --git a/testcases/kernel/uevents/uevent02.c b/testcases/kernel/uevents/uevent02.c index 4355cd8d..abae4f88 100755 --- a/testcases/kernel/uevents/uevent02.c +++ b/testcases/kernel/uevents/uevent02.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Cyril Hrubis + * Copyright (c) Linux Test Project, 2019-2023 */ -/* +/*\ * Very simple uevent netlink socket test. * * We fork a child that listens for a kernel events while parents creates and @@ -24,7 +25,6 @@ #include "uevent.h" #define TUN_PATH "/dev/net/tun" -#define CONFIG_RPS "CONFIG_RPS" #define MAX_UEVENTS 7 static struct uevent_desc add = { @@ -127,10 +127,7 @@ static void verify_uevent(void) static void setup(void) { - struct tst_kconfig_var kconfig = { - .id = CONFIG_RPS, - .id_len = sizeof(CONFIG_RPS) - 1, - }; + struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_RPS"); int i = 0; tst_kconfig_read(&kconfig, 1); diff --git a/testcases/kernel/uevents/uevent03.c b/testcases/kernel/uevents/uevent03.c index ed15fea9..126c924f 100755 --- a/testcases/kernel/uevents/uevent03.c +++ b/testcases/kernel/uevents/uevent03.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Cyril Hrubis + * Copyright (c) Linux Test Project, 2019-2023 */ -/* +/*\ * Very simple uevent netlink socket test. * * We fork a child that listens for a kernel events while parents creates and @@ -25,6 +26,9 @@ static int mouse_fd; static void create_uinput_mouse(void) { mouse_fd = open_uinput(); + if (mouse_fd == -1) + tst_brk(TCONF, "Virtual device is not available"); + setup_mouse_events(mouse_fd); create_input_device(mouse_fd); } diff --git a/testcases/kernel/watchqueue/wqueue01.c b/testcases/kernel/watchqueue/wqueue01.c index 904e512a..2f621ebc 100644 --- a/testcases/kernel/watchqueue/wqueue01.c +++ b/testcases/kernel/watchqueue/wqueue01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if keyctl update is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue02.c b/testcases/kernel/watchqueue/wqueue02.c index 0c3e947d..943a6267 100644 --- a/testcases/kernel/watchqueue/wqueue02.c +++ b/testcases/kernel/watchqueue/wqueue02.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if keyctl unlink is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue03.c b/testcases/kernel/watchqueue/wqueue03.c index c17fc145..d36a201e 100644 --- a/testcases/kernel/watchqueue/wqueue03.c +++ b/testcases/kernel/watchqueue/wqueue03.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if keyctl revoke is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue04.c b/testcases/kernel/watchqueue/wqueue04.c index fc880064..5cb28eb1 100644 --- a/testcases/kernel/watchqueue/wqueue04.c +++ b/testcases/kernel/watchqueue/wqueue04.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if keyctl link is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue05.c b/testcases/kernel/watchqueue/wqueue05.c index 78a4c702..9ef1b581 100644 --- a/testcases/kernel/watchqueue/wqueue05.c +++ b/testcases/kernel/watchqueue/wqueue05.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if keyctl invalidate is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue06.c b/testcases/kernel/watchqueue/wqueue06.c index 2cb6a966..524ba996 100644 --- a/testcases/kernel/watchqueue/wqueue06.c +++ b/testcases/kernel/watchqueue/wqueue06.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if keyctl clear is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue07.c b/testcases/kernel/watchqueue/wqueue07.c index 6c4d1e92..4189600b 100644 --- a/testcases/kernel/watchqueue/wqueue07.c +++ b/testcases/kernel/watchqueue/wqueue07.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if keyctl setperm is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue08.c b/testcases/kernel/watchqueue/wqueue08.c index 4ed9522d..21fbd246 100644 --- a/testcases/kernel/watchqueue/wqueue08.c +++ b/testcases/kernel/watchqueue/wqueue08.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Test if key watch removal is correctly recognized by watch queue. */ diff --git a/testcases/kernel/watchqueue/wqueue09.c b/testcases/kernel/watchqueue/wqueue09.c index 9f077b35..dfda0cc6 100644 --- a/testcases/kernel/watchqueue/wqueue09.c +++ b/testcases/kernel/watchqueue/wqueue09.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Fill the watch queue and wait for a notification loss. */ diff --git a/testcases/lib/.gitignore b/testcases/lib/.gitignore index e8afd06f..385f3c3c 100755 --- a/testcases/lib/.gitignore +++ b/testcases/lib/.gitignore @@ -23,3 +23,5 @@ /tst_sleep /tst_supported_fs /tst_timeout_kill +/tst_res_ +/tst_run_shell diff --git a/testcases/lib/Makefile b/testcases/lib/Makefile index 990b4608..b3a9181c 100755 --- a/testcases/lib/Makefile +++ b/testcases/lib/Makefile @@ -4,6 +4,9 @@ top_srcdir ?= ../.. +LTPLIBS = ujson +tst_run_shell: LTPLDLIBS = -lujson + include $(top_srcdir)/include/mk/testcases.mk INSTALL_TARGETS := *.sh @@ -13,6 +16,7 @@ MAKE_TARGETS := tst_sleep tst_random tst_checkpoint tst_rod tst_kvcmp\ tst_getconf tst_supported_fs tst_check_drivers tst_get_unused_port\ tst_get_median tst_hexdump tst_get_free_pids tst_timeout_kill\ tst_check_kconfigs tst_cgctl tst_fsfreeze tst_ns_create tst_ns_exec\ - tst_ns_ifmove tst_lockdown_enabled tst_secureboot_enabled + tst_ns_ifmove tst_lockdown_enabled tst_secureboot_enabled tst_res_\ + tst_run_shell -include $(top_srcdir)/include/mk/generic_leaf_target.mk +include $(top_srcdir)/include/mk/generic_trunk_target.mk diff --git a/testcases/lib/run_tests.sh b/testcases/lib/run_tests.sh new file mode 100644 index 00000000..5c309bbe --- /dev/null +++ b/testcases/lib/run_tests.sh @@ -0,0 +1,117 @@ +#!/bin/sh +# Copyright (c) 2024 Petr Vorel + +TESTS_PASS=" +shell_loader.sh +shell_loader_all_filesystems.sh +shell_loader_c_child.sh +shell_loader_filesystems.sh +shell_loader_kconfigs.sh +shell_loader_supported_archs.sh +shell_loader_tcnt.sh +shell_loader_cleanup.sh +shell_loader_setup_cleanup.sh +shell_test01 +shell_test02 +shell_test03 +shell_test04 +shell_test05" + +TESTS_FAIL="shell_loader_tags.sh" + +TESTS_TBROK=" +shell_loader_invalid_block.sh +shell_loader_invalid_metadata.sh +shell_loader_no_metadata.sh +shell_loader_wrong_metadata.sh +shell_loader_brk_cleanup.sh" + +TESTS_TCONF="shell_test06" + +FAIL= + +srcdir="$(realpath $(dirname $0))" +builddir="$srcdir" + +usage() +{ + cat << EOF +Usage: $0 [-b DIR ] [-s TESTS] +-b DIR build directory (required for out-of-tree build) +-h print this help +EOF +} + +while getopts b:h opt; do + case $opt in + 'h') usage; exit 0;; + 'b') + builddir="$OPTARG/testcases/lib/" + if [ ! -d "$builddir" ]; then + echo "directory '$builddir' does not exist!" >&2 + exit 1 + fi + ;; + *) usage; runtest_brk TBROK "Error: invalid option";; + esac +done + +# srcdir is for *.sh, builddir for *.c +export PATH="$PATH:$srcdir:$builddir:$srcdir/tests/:$builddir/tests/" + + +tst_mask2flag() +{ + case "$1" in + 0) echo TPASS;; + 1) echo TFAIL;; + 2) echo TBROK;; + 4) echo TWARN;; + 16) echo TINFO;; + 32) echo TCONF;; + esac +} + +run_tests() +{ + local exp="$1" + local test rc + shift + + for test in "$@"; do + printf "\n*** Running '$test' (exp: $(tst_mask2flag $exp)) ***\n" + $test + rc=$? + if [ "$rc" = 127 ]; then + echo "Test '$test' not found, maybe out-of-tree build and unset builddir?" >&2 + exit 1 + elif [ "$rc" != "$exp" ]; then + FAIL="$FAIL\n* $test ($(tst_mask2flag $rc), exp: $(tst_mask2flag $exp))" + fi + done +} + +run_tests 0 $TESTS_PASS +run_tests 1 $TESTS_FAIL +run_tests 2 $TESTS_TBROK +run_tests 32 $TESTS_TCONF + +echo +echo "*** Testing LTP test -h option ***" +echo +run_tests 0 "shell_loader.sh -h" + +echo +echo "*** Testing LTP test -i option ***" +echo +run_tests 0 "shell_loader.sh -i 2" + +echo +echo "***** RESULTS *****" + +if [ "$FAIL" ]; then + printf "Failed tests:$FAIL\n" + exit 1 +fi + +echo "All tests passed" diff --git a/testcases/lib/test.sh b/testcases/lib/test.sh index 8947f47c..d26cf5df 100755 --- a/testcases/lib/test.sh +++ b/testcases/lib/test.sh @@ -108,7 +108,7 @@ tst_require_root() tst_exit() { - if [ -n "${TST_CLEANUP:-}" -a -z "${TST_NO_CLEANUP:-}" ]; then + if [ -n "${TST_CLEANUP:-}" -a -z "${LTP_NO_CLEANUP:-}" ]; then $TST_CLEANUP fi diff --git a/testcases/lib/tests/.gitignore b/testcases/lib/tests/.gitignore new file mode 100644 index 00000000..e9e163d1 --- /dev/null +++ b/testcases/lib/tests/.gitignore @@ -0,0 +1,7 @@ +shell_test01 +shell_test02 +shell_test03 +shell_test04 +shell_test05 +shell_test06 +shell_c_child diff --git a/testcases/commands/eject/Makefile b/testcases/lib/tests/Makefile old mode 100755 new mode 100644 similarity index 63% rename from testcases/commands/eject/Makefile rename to testcases/lib/tests/Makefile index f33b5117..4895b71a --- a/testcases/commands/eject/Makefile +++ b/testcases/lib/tests/Makefile @@ -1,11 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2009, Cisco Systems Inc. -# Ngie Cooper, July 2009 +# Copyright (C) 2024 Cyril Hrubis top_srcdir ?= ../../.. include $(top_srcdir)/include/mk/testcases.mk -INSTALL_TARGETS := eject-tests.sh +INSTALL_TARGETS= include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/lib/tests/shell_c_child.c b/testcases/lib/tests/shell_c_child.c new file mode 100644 index 00000000..fda5133a --- /dev/null +++ b/testcases/lib/tests/shell_c_child.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shell test C child example. + */ + +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" + +int main(void) +{ + tst_reinit(); + + tst_res(TPASS, "C child works fine!"); + + return 0; +} diff --git a/testcases/lib/tests/shell_loader.sh b/testcases/lib/tests/shell_loader.sh new file mode 100644 index 00000000..78dba06b --- /dev/null +++ b/testcases/lib/tests/shell_loader.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# doc +# This is a simple shell test loader example. +# --- +# +# --- +# env +# { +# "needs_tmpdir": true +# } +# --- + +. tst_loader.sh + +tst_test() +{ + tst_res TPASS "Shell loader works fine!" + case "$PWD" in + /tmp/*) + tst_res TPASS "We are running in temp directory in $PWD";; + *) + tst_res TFAIL "We are not running in temp directory but $PWD";; + esac +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_all_filesystems.sh b/testcases/lib/tests/shell_loader_all_filesystems.sh new file mode 100644 index 00000000..3c3978f5 --- /dev/null +++ b/testcases/lib/tests/shell_loader_all_filesystems.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# env +# { +# "needs_root": true, +# "mount_device": true, +# "all_filesystems": true, +# "mntpoint": "ltp_mntpoint" +# } +# --- + +. tst_loader.sh + +tst_test() +{ + local mntpath="$(realpath ltp_mntpoint)" + local mounted="$(grep $mntpath /proc/mounts)" + local device path + + tst_res TINFO "In shell" + + if [ -n "$mounted" ]; then + device=$(echo $mounted |cut -d' ' -f 1) + path=$(echo $mounted |cut -d' ' -f 2) + + tst_res TPASS "$device mounted at $path" + else + tst_res TFAIL "Device not mounted!" + fi +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_brk_cleanup.sh b/testcases/lib/tests/shell_loader_brk_cleanup.sh new file mode 100644 index 00000000..4e8ce4f7 --- /dev/null +++ b/testcases/lib/tests/shell_loader_brk_cleanup.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# env +# { +# } +# --- + +TST_CLEANUP=cleanup + +. tst_loader.sh + +cleanup() +{ + tst_res TINFO "Cleanup runs" +} + +tst_test() +{ + tst_brk TBROK "Test exits" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_c_child.sh b/testcases/lib/tests/shell_loader_c_child.sh new file mode 100644 index 00000000..13480968 --- /dev/null +++ b/testcases/lib/tests/shell_loader_c_child.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# doc +# This is an example how to run C child from shell. +# --- +# +# --- +# env +# { +# } +# --- + +. tst_loader.sh + +tst_test() +{ + if [ -n "LTP_IPC_PATH" ]; then + tst_res TPASS "LTP_IPC_PATH=$LTP_IPC_PATH!" + fi + + tst_res TINFO "Running C child" + shell_c_child +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_cleanup.sh b/testcases/lib/tests/shell_loader_cleanup.sh new file mode 100644 index 00000000..91071a5a --- /dev/null +++ b/testcases/lib/tests/shell_loader_cleanup.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# env +# { +# } +# --- + +TST_CLEANUP=do_cleanup + +. tst_loader.sh + +do_cleanup() +{ + tst_res TINFO "Cleanup executed" +} + +tst_test() +{ + tst_res TPASS "Test is executed" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_filesystems.sh b/testcases/lib/tests/shell_loader_filesystems.sh new file mode 100644 index 00000000..d584503a --- /dev/null +++ b/testcases/lib/tests/shell_loader_filesystems.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# env +# { +# "mount_device": true, +# "mntpoint": "ltp_mntpoint", +# "filesystems": [ +# { +# "type": "tmpfs" +# }, +# { +# "type": "btrfs" +# }, +# { +# "type": "xfs", +# "mkfs_opts": ["-m", "reflink=1"] +# } +# ] +# } +# --- + +. tst_loader.sh + +tst_test() +{ + tst_res TINFO "In shell" + + mntpoint=$(realpath ltp_mntpoint) + mounted=$(grep $mntpoint /proc/mounts) + + if [ -n "$mounted" ]; then + fs=$(echo $mounted |cut -d' ' -f 3) + + tst_res TPASS "Mounted device formatted with $fs" + else + tst_res TFAIL "Device not mounted!" + fi +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_invalid_block.sh b/testcases/lib/tests/shell_loader_invalid_block.sh new file mode 100644 index 00000000..be45303d --- /dev/null +++ b/testcases/lib/tests/shell_loader_invalid_block.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# doc +# This is a simple shell test loader example. +# --- +# +# --- +# env +# { +# "needs_tmpdir": true +# } +# --- +# +# --- +# inv +# +# This is an invalid block that breaks the test. +# --- + +. tst_loader.sh + +tst_test() +{ + tst_res TPASS "This should pass!" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_invalid_metadata.sh b/testcases/lib/tests/shell_loader_invalid_metadata.sh new file mode 100644 index 00000000..c4c75792 --- /dev/null +++ b/testcases/lib/tests/shell_loader_invalid_metadata.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# This test has wrong metadata and should not be run +# +# --- +# env +# { +# {"needs_tmpdir": 42, +# } +# --- +# + +. tst_loader.sh + +tst_test() +{ + tst_res TFAIL "Shell loader should TBROK the test" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_kconfigs.sh b/testcases/lib/tests/shell_loader_kconfigs.sh new file mode 100644 index 00000000..d03bc99c --- /dev/null +++ b/testcases/lib/tests/shell_loader_kconfigs.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# env +# { +# "needs_kconfigs": ["CONFIG_NUMA=y"] +# } +# --- + +. tst_loader.sh + +tst_test() +{ + tst_res TPASS "Shell loader works fine!" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_no_metadata.sh b/testcases/lib/tests/shell_loader_no_metadata.sh new file mode 100644 index 00000000..2ad458ef --- /dev/null +++ b/testcases/lib/tests/shell_loader_no_metadata.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# This test has no metadata and should not be executed +# + +. tst_loader.sh + +tst_test() +{ + tst_res TFAIL "Shell loader should TBROK the test" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_setup_cleanup.sh b/testcases/lib/tests/shell_loader_setup_cleanup.sh new file mode 100644 index 00000000..81842783 --- /dev/null +++ b/testcases/lib/tests/shell_loader_setup_cleanup.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2025 Petr Vorel +# +# --- +# env +# { +# } +# --- + +TST_SETUP=setup +TST_CLEANUP=cleanup + +. tst_loader.sh + +setup() +{ + tst_res TINFO "setup executed" +} + +cleanup() +{ + tst_res TINFO "Cleanup executed" +} + +tst_test() +{ + tst_res TPASS "Test is executed" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_supported_archs.sh b/testcases/lib/tests/shell_loader_supported_archs.sh new file mode 100644 index 00000000..a45cc59b --- /dev/null +++ b/testcases/lib/tests/shell_loader_supported_archs.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# env +# { +# "supported_archs": ["x86", "ppc64", "x86_64"] +# } +# --- + +. tst_loader.sh + +tst_test() +{ + tst_res TPASS "We are running on supported architecture" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_tags.sh b/testcases/lib/tests/shell_loader_tags.sh new file mode 100644 index 00000000..56eadaf7 --- /dev/null +++ b/testcases/lib/tests/shell_loader_tags.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# --- +# env +# { +# "tags": [ +# ["linux-git", "832478cd342ab"], +# ["CVE", "2099-999"] +# ] +# } +# --- + +. tst_loader.sh + +tst_test() +{ + tst_res TFAIL "Fails the test so that tags are shown." +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_tcnt.sh b/testcases/lib/tests/shell_loader_tcnt.sh new file mode 100644 index 00000000..cf2a0b74 --- /dev/null +++ b/testcases/lib/tests/shell_loader_tcnt.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# The script should be executed tcnt times and the iteration number should be in $1 +# +# --- +# env +# { +# "tcnt": 2 +# } +# --- +# + +. tst_loader.sh + +tst_test() +{ + tst_res TPASS "Iteration $1" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_loader_wrong_metadata.sh b/testcases/lib/tests/shell_loader_wrong_metadata.sh new file mode 100644 index 00000000..35c535be --- /dev/null +++ b/testcases/lib/tests/shell_loader_wrong_metadata.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# This test has wrong metadata and should not be run +# +# --- +# env +# { +# "needs_tmpdir": 42, +# } +# --- +# + +. tst_loader.sh + +tst_test() +{ + tst_res TFAIL "Shell loader should TBROK the test" +} + +. tst_run.sh diff --git a/testcases/lib/tests/shell_test01.c b/testcases/lib/tests/shell_test01.c new file mode 100644 index 00000000..b9f07308 --- /dev/null +++ b/testcases/lib/tests/shell_test01.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shell test example. + */ + +#include "tst_test.h" + +static void run_test(void) +{ + tst_run_script("shell_test_pass.sh", NULL); + tst_res(TINFO, "C test exits now"); +} + +static struct tst_test test = { + .runs_script = 1, + .test_all = run_test, +}; diff --git a/testcases/lib/tests/shell_test02.c b/testcases/lib/tests/shell_test02.c new file mode 100644 index 00000000..08705579 --- /dev/null +++ b/testcases/lib/tests/shell_test02.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shell test example. + */ + +#include "tst_test.h" + +static void run_test(void) +{ + tst_run_script("shell_test_pass.sh", NULL); + tst_reap_children(); + tst_res(TINFO, "Shell test has finished at this point!"); +} + +static struct tst_test test = { + .runs_script = 1, + .test_all = run_test, +}; diff --git a/testcases/lib/tests/shell_test03.c b/testcases/lib/tests/shell_test03.c new file mode 100644 index 00000000..61436891 --- /dev/null +++ b/testcases/lib/tests/shell_test03.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shell test example. + */ + +#include +#include "tst_test.h" + +static void run_test(void) +{ + int pid, status; + + pid = tst_run_script("shell_test_pass.sh", NULL); + + tst_res(TINFO, "Waiting for the pid %i", pid); + + waitpid(pid, &status, 0); + + tst_res(TINFO, "Shell test has %s", tst_strstatus(status)); +} + +static struct tst_test test = { + .runs_script = 1, + .test_all = run_test, +}; diff --git a/testcases/lib/tests/shell_test04.c b/testcases/lib/tests/shell_test04.c new file mode 100644 index 00000000..a32dd1e9 --- /dev/null +++ b/testcases/lib/tests/shell_test04.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shell test example. + */ + +#include "tst_test.h" + +static void run_test(void) +{ + char *const params[] = {"param1", "param2", NULL}; + + tst_run_script("shell_test_check_argv.sh", params); +} + +static struct tst_test test = { + .runs_script = 1, + .test_all = run_test, +}; diff --git a/testcases/lib/tests/shell_test05.c b/testcases/lib/tests/shell_test05.c new file mode 100644 index 00000000..771af8fc --- /dev/null +++ b/testcases/lib/tests/shell_test05.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shell test example. + */ + +#include "tst_test.h" + +static void run_test(void) +{ + int pid; + + pid = tst_run_script("shell_test_checkpoint.sh", NULL); + + tst_res(TINFO, "Waiting for shell to sleep on checkpoint!"); + + TST_PROCESS_STATE_WAIT(pid, 'S', 10000); + + tst_res(TINFO, "Waking shell child!"); + + TST_CHECKPOINT_WAKE(0); +} + +static struct tst_test test = { + .runs_script = 1, + .needs_checkpoints = 1, + .test_all = run_test, +}; diff --git a/testcases/lib/tests/shell_test06.c b/testcases/lib/tests/shell_test06.c new file mode 100644 index 00000000..89d66bab --- /dev/null +++ b/testcases/lib/tests/shell_test06.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shell test example. + */ + +#include "tst_test.h" + +static void run_test(void) +{ + tst_run_script("shell_test_brk.sh", NULL); +} + +static struct tst_test test = { + .runs_script = 1, + .test_all = run_test, +}; diff --git a/testcases/lib/tests/shell_test_brk.sh b/testcases/lib/tests/shell_test_brk.sh new file mode 100644 index 00000000..d0c72c31 --- /dev/null +++ b/testcases/lib/tests/shell_test_brk.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis + +. tst_env.sh + +tst_brk TCONF "This exits test and the next message should not be reached" +tst_res TFAIL "If you see this the test failed" diff --git a/testcases/lib/tests/shell_test_check_argv.sh b/testcases/lib/tests/shell_test_check_argv.sh new file mode 100644 index 00000000..ea2fc1bb --- /dev/null +++ b/testcases/lib/tests/shell_test_check_argv.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis + +. tst_env.sh + +tst_res TINFO "argv = $@" + +if [ $# -ne 2 ]; then + tst_res TFAIL "Wrong number of parameters got $# expected 2" +else + tst_res TPASS "Got 2 parameters" +fi + +if [ "$1" != "param1" ]; then + tst_res TFAIL "First parameter is $1 expected param1" +else + tst_res TPASS "First parameter is $1" +fi + +if [ "$2" != "param2" ]; then + tst_res TFAIL "Second parameter is $2 expected param2" +else + tst_res TPASS "Second parameter is $2" +fi diff --git a/testcases/lib/tests/shell_test_checkpoint.sh b/testcases/lib/tests/shell_test_checkpoint.sh new file mode 100644 index 00000000..18a3ac3b --- /dev/null +++ b/testcases/lib/tests/shell_test_checkpoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis + +. tst_env.sh + +tst_res TINFO "Waiting for a checkpoint 0" +tst_checkpoint wait 10000 0 +tst_res TPASS "Continuing after checkpoint" diff --git a/testcases/lib/tests/shell_test_pass.sh b/testcases/lib/tests/shell_test_pass.sh new file mode 100644 index 00000000..ad81fea7 --- /dev/null +++ b/testcases/lib/tests/shell_test_pass.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis + +. tst_env.sh + +tst_res TPASS "This is called from the shell script!" +tst_sleep 100ms diff --git a/testcases/lib/tst_env.sh b/testcases/lib/tst_env.sh new file mode 100644 index 00000000..585790a7 --- /dev/null +++ b/testcases/lib/tst_env.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# This is a minimal test environment for a shell scripts executed from C by +# tst_run_shell() function. Shell tests must use the tst_loader.sh instead! +# + +tst_script_name=$(basename $0) + +# bash does not expand aliases in non-iteractive mode, enable it +if [ -n "$BASH_VERSION" ]; then + shopt -s expand_aliases +fi + +# dash does not support line numbers even though this is mandated by POSIX +if [ -z "$LINENO" ]; then + LINENO=-1 +fi + +tst_brk_() +{ + tst_res_ "$@" + + case "$3" in + "TBROK") exit 2;; + *) exit 0;; + esac +} + +alias tst_res="tst_res_ $tst_script_name \$LINENO" +alias tst_brk="tst_brk_ $tst_script_name \$LINENO" diff --git a/testcases/lib/tst_loader.sh b/testcases/lib/tst_loader.sh new file mode 100644 index 00000000..62c9cc6d --- /dev/null +++ b/testcases/lib/tst_loader.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024-2025 Cyril Hrubis +# +# This is a loader for shell tests that use the C test library. +# + +if [ -z "$LTP_IPC_PATH" ]; then + tst_run_shell $(basename "$0") "$@" + exit $? +else + . tst_env.sh +fi diff --git a/testcases/lib/tst_net.sh b/testcases/lib/tst_net.sh index 6168db86..6c227831 100755 --- a/testcases/lib/tst_net.sh +++ b/testcases/lib/tst_net.sh @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2014-2017 Oracle and/or its affiliates. All Rights Reserved. -# Copyright (c) 2016-2023 Petr Vorel +# Copyright (c) 2016-2024 Petr Vorel # Author: Alexey Kodanev [ -n "$TST_LIB_NET_LOADED" ] && return 0 @@ -175,7 +175,9 @@ init_ltp_netspace() tst_require_cmds ip tst_ns_create tst_ns_exec tst_ns_ifmove tst_require_root - tst_require_drivers veth + if [ -z "$TST_USE_LEGACY_API" ]; then + tst_require_drivers veth + fi ROD ip link add name ltp_ns_veth1 type veth peer name ltp_ns_veth2 pid="$(ROD tst_ns_create net,mnt)" mkdir -p /var/run/netns @@ -263,7 +265,7 @@ tst_rhost_run() if [ $ret -eq 1 ]; then output=$(echo "$output" | sed 's/RTERR//') [ "$safe" ] && \ - tst_brk_ TBROK "'$cmd' failed on '$RHOST': '$output'" + tst_brk_ TBROK "'$cmd' failed on $use: '$output'" fi [ -z "$out" -a -n "$output" ] && echo "$output" @@ -745,12 +747,12 @@ tst_netload() fi OPTIND=0 - while getopts :a:H:d:n:N:r:R:S:b:t:T:fFe:m:A:D: opt; do + while getopts :a:c:H:n:N:r:R:S:b:t:T:fFe:m:A:D: opt; do case "$opt" in a) c_num="$OPTARG" ;; H) c_opts="${c_opts}-H $OPTARG " hostopt="$OPTARG" ;; - d) rfile="$OPTARG" ;; + c) rfile="$OPTARG" ;; n) c_opts="${c_opts}-n $OPTARG " ;; N) c_opts="${c_opts}-N $OPTARG " ;; r) c_requests="$OPTARG" ;; @@ -766,7 +768,7 @@ tst_netload() f) cs_opts="${cs_opts}-f " ;; F) cs_opts="${cs_opts}-F " ;; e) expect_res="$OPTARG" ;; - D) [ "$TST_NETLOAD_BINDTODEVICE" = 1 ] && cs_opts="${cs_opts}-D $OPTARG " + D) [ "$TST_NETLOAD_BINDTODEVICE" = 1 ] && cs_opts="${cs_opts}-d $OPTARG " bind_to_device=0 ;; *) tst_brk_ TBROK "tst_netload: unknown option: $OPTARG" ;; esac @@ -776,8 +778,8 @@ tst_netload() [ "$setup_srchost" = 1 ] && s_opts="${s_opts}-S $hostopt " if [ "$bind_to_device" = 1 -a "$TST_NETLOAD_BINDTODEVICE" = 1 ]; then - c_opts="${c_opts}-D $(tst_iface) " - s_opts="${s_opts}-D $(tst_iface rhost) " + c_opts="${c_opts}-d $(tst_iface) " + s_opts="${s_opts}-d $(tst_iface rhost) " fi local expect_ret=0 @@ -790,7 +792,7 @@ tst_netload() fi s_opts="${cs_opts}${s_opts}-R $s_replies -B $TST_TMPDIR" - c_opts="${cs_opts}${c_opts}-a $c_num -r $((c_requests / run_cnt)) -d $PWD/$rfile" + c_opts="${cs_opts}${c_opts}-a $c_num -r $((c_requests / run_cnt)) -c $PWD/$rfile" tst_res_ TINFO "run server 'netstress $s_opts'" tst_res_ TINFO "run client 'netstress -l $c_opts' $run_cnt times" @@ -863,22 +865,33 @@ tst_netload() # TIME: time that is compared to the base one # THRESHOD_LOW: lower limit for TFAIL # THRESHOD_HIGH: upper limit for TWARN +# +# Slow performance can be ignored with setting environment variable +# LTP_NET_FEATURES_IGNORE_PERFORMANCE_FAILURE=1 tst_netload_compare() { local base_time=$1 local new_time=$2 local threshold_low=$3 local threshold_hi=$4 + local ttype='TFAIL' + local msg res if [ -z "$base_time" -o -z "$new_time" -o -z "$threshold_low" ]; then tst_brk_ TBROK "tst_netload_compare: invalid argument(s)" fi - local res=$(((base_time - new_time) * 100 / base_time)) - local msg="performance result is ${res}%" + res=$(((base_time - new_time) * 100 / base_time)) + msg="performance result is ${res}%" if [ "$res" -lt "$threshold_low" ]; then - tst_res_ TFAIL "$msg < threshold ${threshold_low}%" + if [ "$LTP_NET_FEATURES_IGNORE_PERFORMANCE_FAILURE" = 1 ]; then + ttype='TINFO'; + tst_res_ TINFO "WARNING: slow performance is not treated as error due LTP_NET_FEATURES_IGNORE_PERFORMANCE_FAILURE=1" + else + tst_res_ TINFO "Following slow performance can be ignored with LTP_NET_FEATURES_IGNORE_PERFORMANCE_FAILURE=1" + fi + tst_res_ $ttype "$msg < threshold ${threshold_low}%" return fi @@ -917,7 +930,10 @@ tst_ping() c) ping_count="$OPTARG";; s) msg_sizes="$OPTARG";; p) pattern_opt="-p $OPTARG";; - I) src_iface="$OPTARG";; + I) src_iface="$OPTARG" + tst_ping_opt_unsupported -I $OPTARG && \ + tst_brk_ TCONF "unsupported ping version (ping from inetutils?)" + ;; H) dst_addr="$OPTARG";; *) tst_brk_ TBROK "tst_ping: unknown option: $OPTARG";; esac diff --git a/testcases/lib/tst_ns_create.c b/testcases/lib/tst_ns_create.c index 1c6258cd..90862d9d 100644 --- a/testcases/lib/tst_ns_create.c +++ b/testcases/lib/tst_ns_create.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Creates a child process in the new specified namespace(s), child is then * daemonized and is running in the background. PID of the daemonized child * process is printed on the stdout. As the new namespace(s) is(are) maintained @@ -23,12 +21,6 @@ #include "tst_test.h" #include "tst_ns_common.h" -extern struct tst_test *tst_test; - -static struct tst_test test = { - .forks_child = 1, /* Needed by SAFE_CLONE */ -}; - static void print_help(void) { int i; @@ -57,7 +49,7 @@ static void child_fn(void) int main(int argc, char *argv[]) { - struct tst_clone_args args = { 0, SIGCHLD }; + struct tst_clone_args args = { .exit_signal = SIGCHLD }; char *token; int pid; @@ -66,8 +58,6 @@ int main(int argc, char *argv[]) return 1; } - tst_test = &test; - while ((token = strsep(&argv[1], ","))) { struct param *p = get_param(token); @@ -80,7 +70,12 @@ int main(int argc, char *argv[]) args.flags |= p->flag; } - pid = SAFE_CLONE(&args); + pid = tst_clone(&args); + if (pid < 0) { + printf("clone() failed"); + return 1; + } + if (!pid) { child_fn(); return 0; diff --git a/testcases/lib/tst_ns_exec.c b/testcases/lib/tst_ns_exec.c index 66a4e69d..d5011231 100644 --- a/testcases/lib/tst_ns_exec.c +++ b/testcases/lib/tst_ns_exec.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Enters the namespace(s) of a process specified by a PID and then executes * the indicated program inside that namespace(s). */ @@ -20,12 +18,6 @@ #include "tst_test.h" #include "tst_ns_common.h" -extern struct tst_test *tst_test; - -static struct tst_test test = { - .forks_child = 1, /* Needed by SAFE_CLONE */ -}; - static int ns_fd[NS_TOTAL]; static int ns_fds; @@ -67,12 +59,10 @@ static void close_ns_fd(void) int main(int argc, char *argv[]) { - struct tst_clone_args args = { 0, SIGCHLD }; + struct tst_clone_args args = { .exit_signal = SIGCHLD }; int i, status, pid; char *token; - tst_test = &test; - if (argc < 4) { print_help(); return 1; @@ -100,7 +90,12 @@ int main(int argc, char *argv[]) for (i = 0; i < ns_fds; i++) SAFE_SETNS(ns_fd[i], 0); - pid = SAFE_CLONE(&args); + pid = tst_clone(&args); + if (pid < 0) { + printf("clone() failed"); + return 1; + } + if (!pid) SAFE_EXECVP(argv[3], argv+3); diff --git a/testcases/lib/tst_ns_ifmove.c b/testcases/lib/tst_ns_ifmove.c index 03531fc9..ba1d2340 100644 --- a/testcases/lib/tst_ns_ifmove.c +++ b/testcases/lib/tst_ns_ifmove.c @@ -6,8 +6,6 @@ */ /*\ - * [Description] - * * Moves a network interface to the namespace of a process specified by a PID. */ diff --git a/testcases/lib/tst_res_.c b/testcases/lib/tst_res_.c new file mode 100644 index 00000000..b12e231a --- /dev/null +++ b/testcases/lib/tst_res_.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + * Copyright (c) Linux Test Project, 2025 + */ + +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" + +static void print_help(void) +{ + printf("Usage: tst_{res,brk} filename lineno [TPASS|TBROK|TFAIL|TWARN|TCONF|TINFO|TDEBUG] 'A short description'\n"); +} + +int main(int argc, char *argv[]) +{ + int i, lineno, type; + + if (argc < 5) { + fprintf(stderr, "%s:%d: invalid argc=%i, expected 5\n", __FILE__, __LINE__, argc); + goto help; + } + + lineno = atoi(argv[2]); + + if (!strcmp(argv[3], "TPASS")) { + type = TPASS; + } else if (!strcmp(argv[3], "TFAIL")) { + type = TFAIL; + } else if (!strcmp(argv[3], "TCONF")) { + type = TCONF; + } else if (!strcmp(argv[3], "TINFO")) { + type = TINFO; + } else if (!strcmp(argv[3], "TWARN")) { + type = TWARN; + } else if (!strcmp(argv[3], "TDEBUG")) { + type = TDEBUG; + } else if (!strcmp(argv[3], "TBROK")) { + type = TBROK; + } else { + fprintf(stderr, "%s:%d: Wrong type '%s' (invoked by %s:%d)\n", __FILE__, + __LINE__, argv[3], argv[1], lineno); + goto help; + } + + size_t len = 0; + + for (i = 4; i < argc; i++) + len += strlen(argv[i]) + 1; + + char *msg = SAFE_MALLOC(len); + char *msgp = msg; + + for (i = 4; i < argc; i++) { + msgp = strcpy(msgp, argv[i]); + msgp += strlen(argv[i]); + *(msgp++) = ' '; + } + + *(msgp - 1) = 0; + + tst_reinit(); + + tst_res_(argv[1], lineno, type, "%s", msg); + + return 0; +help: + print_help(); + return 1; +} diff --git a/testcases/lib/tst_run.sh b/testcases/lib/tst_run.sh new file mode 100644 index 00000000..841b5fd1 --- /dev/null +++ b/testcases/lib/tst_run.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2025 Cyril Hrubis +# Copyright (c) 2025 Petr Vorel + +. tst_env.sh + +if [ -n "$TST_CLEANUP" ]; then + if command -v $TST_CLEANUP >/dev/null 2>/dev/null; then + trap $TST_CLEANUP EXIT + else + tst_res TWARN "TST_CLEANUP=$TST_CLEANUP declared, but function not defined (or cmd not found)" + fi +fi + +if [ -n "$TST_SETUP" ]; then + if command -v $TST_SETUP >/dev/null 2>/dev/null; then + $TST_SETUP + else + tst_brk TBROK "TST_SETUP=$TST_SETUP declared, but function not defined (or cmd not found)" + fi +fi + +tst_test diff --git a/testcases/lib/tst_run_shell.c b/testcases/lib/tst_run_shell.c new file mode 100644 index 00000000..7a446e00 --- /dev/null +++ b/testcases/lib/tst_run_shell.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Cyril Hrubis + */ +#include + +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" +#include "tst_safe_stdio.h" +#include "ujson.h" + +static char *shell_filename; + +static void run_shell(void) +{ + tst_run_script(shell_filename, NULL); +} + +static void run_shell_tcnt(unsigned int n) +{ + char buf[128]; + char *const params[] = {buf, NULL}; + + snprintf(buf, sizeof(buf), "%u", n); + + tst_run_script(shell_filename, params); +} + +static struct tst_test test = { + .runs_script = 1, +}; + +static void print_help(void) +{ + printf("Usage: tst_shell_loader ltp_shell_test.sh ...\n"); +} + +static char *metadata; +static size_t metadata_size; +static size_t metadata_used; + +static void metadata_append(const char *line) +{ + size_t linelen = strlen(line); + + if (metadata_size - metadata_used < linelen + 1) { + metadata_size += 4096; + metadata = SAFE_REALLOC(metadata, metadata_size); + } + + strcpy(metadata + metadata_used, line); + metadata_used += linelen; +} + +enum test_attr_ids { + ALL_FILESYSTEMS, + DEV_MIN_SIZE, + FILESYSTEMS, + FORMAT_DEVICE, + MIN_CPUS, + MIN_MEM_AVAIL, + MIN_KVER, + MIN_SWAP_AVAIL, + MNTPOINT, + MOUNT_DEVICE, + NEEDS_ABI_BITS, + NEEDS_CMDS, + NEEDS_DEVFS, + NEEDS_DEVICE, + NEEDS_DRIVERS, + NEEDS_HUGETLBFS, + NEEDS_KCONFIGS, + NEEDS_ROFS, + NEEDS_ROOT, + NEEDS_TMPDIR, + RESTORE_WALLCLOCK, + SAVE_RESTORE, + SKIP_FILESYSTEMS, + SKIP_IN_COMPAT, + SKIP_IN_LOCKDOWN, + SKIP_IN_SECUREBOOT, + SUPPORTED_ARCHS, + TAGS, + TAINT_CHECK, + TCNT, +}; + +static ujson_obj_attr test_attrs[] = { + UJSON_OBJ_ATTR_IDX(ALL_FILESYSTEMS, "all_filesystems", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(DEV_MIN_SIZE, "dev_min_size", UJSON_INT), + UJSON_OBJ_ATTR_IDX(FILESYSTEMS, "filesystems", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(FORMAT_DEVICE, "format_device", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(MIN_CPUS, "min_cpus", UJSON_INT), + UJSON_OBJ_ATTR_IDX(MIN_MEM_AVAIL, "min_mem_avail", UJSON_INT), + UJSON_OBJ_ATTR_IDX(MIN_KVER, "min_kver", UJSON_STR), + UJSON_OBJ_ATTR_IDX(MIN_SWAP_AVAIL, "min_swap_avail", UJSON_INT), + UJSON_OBJ_ATTR_IDX(MNTPOINT, "mntpoint", UJSON_STR), + UJSON_OBJ_ATTR_IDX(MOUNT_DEVICE, "mount_device", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(NEEDS_ABI_BITS, "needs_abi_bits", UJSON_INT), + UJSON_OBJ_ATTR_IDX(NEEDS_CMDS, "needs_cmds", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(NEEDS_DEVFS, "needs_devfs", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(NEEDS_DEVICE, "needs_device", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(NEEDS_DRIVERS, "needs_drivers", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(NEEDS_HUGETLBFS, "needs_hugetlbfs", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(NEEDS_KCONFIGS, "needs_kconfigs", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(NEEDS_ROFS, "needs_rofs", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(NEEDS_ROOT, "needs_root", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(NEEDS_TMPDIR, "needs_tmpdir", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(RESTORE_WALLCLOCK, "restore_wallclock", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(SAVE_RESTORE, "save_restore", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(SKIP_FILESYSTEMS, "skip_filesystems", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(SKIP_IN_COMPAT, "skip_in_compat", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(SKIP_IN_LOCKDOWN, "skip_in_lockdown", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(SKIP_IN_SECUREBOOT, "skip_in_secureboot", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(SUPPORTED_ARCHS, "supported_archs", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(TAGS, "tags", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(TAINT_CHECK, "taint_check", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(TCNT, "tcnt", UJSON_INT) +}; + +static ujson_obj test_obj = { + .attrs = test_attrs, + .attr_cnt = UJSON_ARRAY_SIZE(test_attrs), +}; + +static const char *const *parse_strarr(ujson_reader *reader, ujson_val *val) +{ + unsigned int cnt = 0, i = 0; + char **ret; + + ujson_reader_state state = ujson_reader_state_save(reader); + + UJSON_ARR_FOREACH(reader, val) { + if (val->type != UJSON_STR) { + ujson_err(reader, "Expected string!"); + return NULL; + } + + cnt++; + } + + ujson_reader_state_load(reader, state); + + ret = SAFE_MALLOC(sizeof(char *) * (cnt + 1)); + + UJSON_ARR_FOREACH(reader, val) { + ret[i++] = strdup(val->val_str); + } + + ret[i] = NULL; + + return (const char *const *)ret; +} + +enum fs_ids { + FS_MIN_KVER, + MKFS_OPTS, + MKFS_SIZE_OPT, + MKFS_VER, + MNT_FLAGS, + TYPE, +}; + +static ujson_obj_attr fs_attrs[] = { + UJSON_OBJ_ATTR_IDX(FS_MIN_KVER, "min_kver", UJSON_STR), + UJSON_OBJ_ATTR_IDX(MKFS_OPTS, "mkfs_opts", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(MKFS_SIZE_OPT, "mkfs_size_opt", UJSON_STR), + UJSON_OBJ_ATTR_IDX(MKFS_VER, "mkfs_ver", UJSON_STR), + UJSON_OBJ_ATTR_IDX(MNT_FLAGS, "mnt_flags", UJSON_ARR), + UJSON_OBJ_ATTR_IDX(TYPE, "type", UJSON_STR), +}; + +static ujson_obj fs_obj = { + .attrs = fs_attrs, + .attr_cnt = UJSON_ARRAY_SIZE(fs_attrs), +}; + +static int parse_mnt_flags(ujson_reader *reader, ujson_val *val) +{ + int ret = 0; + + UJSON_ARR_FOREACH(reader, val) { + if (val->type != UJSON_STR) { + ujson_err(reader, "Expected string!"); + return ret; + } + + if (!strcmp(val->val_str, "RDONLY")) + ret |= MS_RDONLY; + else if (!strcmp(val->val_str, "NOATIME")) + ret |= MS_NOATIME; + else if (!strcmp(val->val_str, "NOEXEC")) + ret |= MS_NOEXEC; + else if (!strcmp(val->val_str, "NOSUID")) + ret |= MS_NOSUID; + else + ujson_err(reader, "Invalid mount flag"); + } + + return ret; +} + +static struct tst_fs *parse_filesystems(ujson_reader *reader, ujson_val *val) +{ + unsigned int i = 0, cnt = 0; + struct tst_fs *ret; + + ujson_reader_state state = ujson_reader_state_save(reader); + + UJSON_ARR_FOREACH(reader, val) { + if (val->type != UJSON_OBJ) { + ujson_err(reader, "Expected object!"); + return NULL; + } + ujson_obj_skip(reader); + cnt++; + } + + ujson_reader_state_load(reader, state); + + ret = SAFE_MALLOC(sizeof(struct tst_fs) * (cnt + 1)); + memset(ret, 0, sizeof(*ret) * (cnt+1)); + + UJSON_ARR_FOREACH(reader, val) { + UJSON_OBJ_FOREACH_FILTER(reader, val, &fs_obj, ujson_empty_obj) { + switch ((enum fs_ids)val->idx) { + case MKFS_OPTS: + ret[i].mkfs_opts = parse_strarr(reader, val); + break; + case MKFS_SIZE_OPT: + ret[i].mkfs_size_opt = strdup(val->val_str); + break; + case MKFS_VER: + ret[i].mkfs_ver = strdup(val->val_str); + break; + case MNT_FLAGS: + ret[i].mnt_flags = parse_mnt_flags(reader, val); + break; + case TYPE: + ret[i].type = strdup(val->val_str); + break; + case FS_MIN_KVER: + ret[i].min_kver = strdup(val->val_str); + break; + } + + } + + i++; + } + + return ret; +} + +static struct tst_tag *parse_tags(ujson_reader *reader, ujson_val *val) +{ + unsigned int i = 0, cnt = 0; + struct tst_tag *ret; + + ujson_reader_state state = ujson_reader_state_save(reader); + + UJSON_ARR_FOREACH(reader, val) { + if (val->type != UJSON_ARR) { + ujson_err(reader, "Expected array!"); + return NULL; + } + ujson_arr_skip(reader); + cnt++; + } + + ujson_reader_state_load(reader, state); + + ret = SAFE_MALLOC(sizeof(struct tst_tag) * (cnt + 1)); + memset(&ret[cnt], 0, sizeof(ret[cnt])); + + UJSON_ARR_FOREACH(reader, val) { + char *name = NULL; + char *value = NULL; + + UJSON_ARR_FOREACH(reader, val) { + if (val->type != UJSON_STR) { + ujson_err(reader, "Expected string!"); + return NULL; + } + + if (!name) { + name = strdup(val->val_str); + } else if (!value) { + value = strdup(val->val_str); + } else { + ujson_err(reader, "Expected only two members!"); + return NULL; + } + } + + ret[i].name = name; + ret[i].value = value; + i++; + } + + return ret; +} + +static struct tst_path_val *parse_save_restore(ujson_reader *reader, ujson_val *val) +{ + unsigned int i = 0, cnt = 0; + struct tst_path_val *ret; + + ujson_reader_state state = ujson_reader_state_save(reader); + + UJSON_ARR_FOREACH(reader, val) { + if (val->type != UJSON_ARR) { + ujson_err(reader, "Expected array!"); + return NULL; + } + ujson_arr_skip(reader); + cnt++; + } + + ujson_reader_state_load(reader, state); + + ret = SAFE_MALLOC(sizeof(struct tst_path_val) * (cnt + 1)); + memset(&ret[cnt], 0, sizeof(ret[cnt])); + + UJSON_ARR_FOREACH(reader, val) { + char *path = NULL; + char *pval = NULL; + int flags_set = 0; + int val_set = 0; + unsigned int flags = 0; + + UJSON_ARR_FOREACH(reader, val) { + if (!path) { + if (val->type != UJSON_STR) { + ujson_err(reader, "Expected string!"); + return NULL; + } + + path = strdup(val->val_str); + } else if (!val_set) { + if (val->type == UJSON_STR) { + pval = strdup(val->val_str); + } else if (val->type != UJSON_NULL) { + ujson_err(reader, "Expected string or NULL!"); + return NULL; + } + val_set = 1; + } else if (!flags_set) { + if (val->type != UJSON_STR) { + ujson_err(reader, "Expected string!"); + return NULL; + } + + if (!strcmp(val->val_str, "TCONF")) { + flags = TST_SR_TCONF; + } else if (!strcmp(val->val_str, "TBROK")) { + flags = TST_SR_TBROK; + } else if (!strcmp(val->val_str, "SKIP")) { + flags = TST_SR_SKIP; + } else { + ujson_err(reader, "Invalid flags!"); + return NULL; + } + + flags_set = 1; + } else { + ujson_err(reader, "Expected only two members!"); + return NULL; + } + } + + if (!path || !flags_set) { + ujson_err(reader, "Expected [\"/{proc,sys}/path\", {\"TCONF\", \"TBROK\", \"TSKIP\"}]!"); + return NULL; + } + + ret[i].path = path; + ret[i].val = pval; + ret[i].flags = flags; + i++; + } + + return ret; +} + +static void parse_metadata(void) +{ + ujson_reader reader = UJSON_READER_INIT(metadata, metadata_used, UJSON_READER_STRICT); + char str_buf[128]; + ujson_val val = UJSON_VAL_INIT(str_buf, sizeof(str_buf)); + + UJSON_OBJ_FOREACH_FILTER(&reader, &val, &test_obj, ujson_empty_obj) { + switch ((enum test_attr_ids)val.idx) { + case ALL_FILESYSTEMS: + test.all_filesystems = val.val_bool; + break; + case DEV_MIN_SIZE: + if (val.val_int <= 0) + ujson_err(&reader, "Device size must be > 0"); + else + test.dev_min_size = val.val_int; + break; + case FILESYSTEMS: + test.filesystems = parse_filesystems(&reader, &val); + break; + case FORMAT_DEVICE: + test.format_device = val.val_bool; + break; + case MIN_CPUS: + if (val.val_int <= 0) + ujson_err(&reader, "Minimal number of cpus must be > 0"); + else + test.min_cpus = val.val_int; + break; + case MIN_MEM_AVAIL: + if (val.val_int <= 0) + ujson_err(&reader, "Minimal available memory size must be > 0"); + else + test.min_mem_avail = val.val_int; + break; + case MIN_KVER: + test.min_kver = strdup(val.val_str); + break; + case MIN_SWAP_AVAIL: + if (val.val_int <= 0) + ujson_err(&reader, "Minimal available swap size must be > 0"); + else + test.min_swap_avail = val.val_int; + break; + case MNTPOINT: + test.mntpoint = strdup(val.val_str); + break; + case MOUNT_DEVICE: + test.mount_device = val.val_bool; + break; + case NEEDS_ABI_BITS: + if (val.val_int == 32 || val.val_int == 64) + test.needs_abi_bits = val.val_int; + else + ujson_err(&reader, "ABI bits must be 32 or 64"); + break; + case NEEDS_CMDS: + test.needs_cmds = parse_strarr(&reader, &val); + break; + case NEEDS_DEVFS: + test.needs_devfs = val.val_bool; + break; + case NEEDS_DEVICE: + test.needs_device = val.val_bool; + break; + case NEEDS_DRIVERS: + test.needs_drivers = parse_strarr(&reader, &val); + break; + case NEEDS_HUGETLBFS: + test.needs_hugetlbfs = val.val_bool; + break; + case NEEDS_KCONFIGS: + test.needs_kconfigs = parse_strarr(&reader, &val); + break; + case NEEDS_ROFS: + test.needs_rofs = val.val_bool; + break; + case NEEDS_ROOT: + test.needs_root = val.val_bool; + break; + case NEEDS_TMPDIR: + test.needs_tmpdir = val.val_bool; + break; + case RESTORE_WALLCLOCK: + test.restore_wallclock = val.val_bool; + break; + case SAVE_RESTORE: + test.save_restore = parse_save_restore(&reader, &val); + break; + case SKIP_FILESYSTEMS: + test.skip_filesystems = parse_strarr(&reader, &val); + break; + case SKIP_IN_COMPAT: + test.skip_in_compat = val.val_bool; + break; + case SKIP_IN_LOCKDOWN: + test.skip_in_lockdown = val.val_bool; + break; + case SKIP_IN_SECUREBOOT: + test.skip_in_secureboot = val.val_bool; + break; + case SUPPORTED_ARCHS: + test.supported_archs = parse_strarr(&reader, &val); + break; + case TAGS: + test.tags = parse_tags(&reader, &val); + break; + case TAINT_CHECK: + test.taint_check = val.val_bool; + break; + case TCNT: + if (val.val_int <= 0) + ujson_err(&reader, "Number of tests must be > 0"); + else + test.tcnt = val.val_int; + break; + } + } + + ujson_reader_finish(&reader); + + if (ujson_reader_err(&reader)) + tst_brk(TBROK, "Invalid metadata"); +} + +enum parser_state { + PAR_NONE, + PAR_ESC, + PAR_DOC, + PAR_ENV, +}; + +static void extract_metadata(void) +{ + FILE *f; + char line[4096]; + char path[4096]; + enum parser_state state = PAR_NONE; + unsigned int lineno = 1; + + if (tst_get_path(shell_filename, path, sizeof(path)) == -1) + tst_brk(TBROK, "Failed to find %s in $PATH", shell_filename); + + f = SAFE_FOPEN(path, "r"); + + while (fgets(line, sizeof(line), f)) { + switch (state) { + case PAR_NONE: + if (!strcmp(line, "# ---\n")) + state = PAR_ESC; + break; + case PAR_ESC: + if (!strcmp(line, "# env\n")) { + state = PAR_ENV; + } else if (!strcmp(line, "# doc\n")) { + state = PAR_DOC; + } else { + tst_brk(TBROK, "%s: %u: Unknown comment block %s", + path, lineno, line); + } + break; + case PAR_ENV: + if (line[0] != '#') { + tst_brk(TBROK, + "%s: %u: Unexpected end of comment block!", + path, lineno); + } + + if (!strcmp(line, "# ---\n")) + state = PAR_NONE; + else + metadata_append(line + 2); + break; + case PAR_DOC: + if (line[0] != '#') { + tst_brk(TBROK, + "%s: %u: Unexpected end of comment block!", + path, lineno); + } + + if (!strcmp(line, "# ---\n")) + state = PAR_NONE; + break; + } + + lineno++; + } + + fclose(f); +} + +static void prepare_test_struct(void) +{ + extract_metadata(); + + if (metadata) + parse_metadata(); + else + tst_brk(TBROK, "No metadata found!"); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) + goto help; + + shell_filename = argv[1]; + + prepare_test_struct(); + + if (test.tcnt) + test.test = run_shell_tcnt; + else + test.test_all = run_shell; + + tst_run_tcases(argc - 1, argv + 1, &test); +help: + print_help(); + return 1; +} diff --git a/testcases/lib/tst_security.sh b/testcases/lib/tst_security.sh index 05640234..820736c7 100755 --- a/testcases/lib/tst_security.sh +++ b/testcases/lib/tst_security.sh @@ -127,8 +127,7 @@ tst_get_selinux_dir() { local dir="/sys/fs/selinux" - [ -d "$dir" ] || dir="/selinux" - [ -d "$dir" ] && echo "$dir" + [ -f "$dir/enforce" ] && echo "$dir" } # Get SELinux enforce file path @@ -143,11 +142,10 @@ tst_get_enforce() tst_update_selinux_state() { - local cur_val new_val + local val local dir=$(tst_get_selinux_dir) - [ -z "$dir" ] || return 1 + [ -n "$dir" ] || return 1 - cur_val=$(cat $dir/checkreqprot) - [ $cur_val = 1 ] && new_val=0 || new_val=1 - echo $new_val > $dir/checkreqprot + val=$(cat $dir/checkreqprot) + echo $val > $dir/checkreqprot } diff --git a/testcases/lib/tst_supported_fs.c b/testcases/lib/tst_supported_fs.c index 1832154f..af02a9cc 100755 --- a/testcases/lib/tst_supported_fs.c +++ b/testcases/lib/tst_supported_fs.c @@ -14,6 +14,7 @@ #define TST_NO_DEFAULT_MAIN #include "tst_test.h" #include "tst_fs.h" +#include "tst_private.h" #define err_exit(...) ({ \ fprintf(stderr, __VA_ARGS__); \ diff --git a/testcases/lib/tst_test.sh b/testcases/lib/tst_test.sh index b5b38f52..4be10a4f 100755 --- a/testcases/lib/tst_test.sh +++ b/testcases/lib/tst_test.sh @@ -1,6 +1,6 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) Linux Test Project, 2014-2022 +# Copyright (c) Linux Test Project, 2014-2025 # Author: Cyril Hrubis # # LTP test library for shell. @@ -26,8 +26,9 @@ trap "unset _tst_setup_timer_pid; tst_brk TBROK 'test terminated'" TERM _tst_do_cleanup() { - if [ -n "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" -a -z "$TST_NO_CLEANUP" ]; then + if [ -n "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" -a -z "$LTP_NO_CLEANUP" ]; then if command -v $TST_CLEANUP >/dev/null 2>/dev/null; then + TST_DO_CLEANUP= $TST_CLEANUP else tst_res TWARN "TST_CLEANUP=$TST_CLEANUP declared, but function not defined (or cmd not found)" @@ -80,7 +81,7 @@ _tst_do_exit() fi if [ $TST_BROK -gt 0 -o $TST_FAIL -gt 0 -o $TST_WARN -gt 0 ]; then - _tst_check_security_modules + [ -z "$TST_SKIP_LSM_WARNINGS" ] && _tst_check_security_modules fi cat >&2 << EOF @@ -126,12 +127,19 @@ tst_brk() local res=$1 shift - if [ "$TST_DO_EXIT" = 1 ]; then + # TBROK => TWARN on cleanup or exit + if [ "$res" = TBROK ] && [ "$TST_DO_EXIT" = 1 -o -z "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" ]; then tst_res TWARN "$@" + TST_DO_CLEANUP= return fi - tst_res "$res" "$@" + if [ "$res" != TBROK -a "$res" != TCONF ]; then + tst_res TBROK "tst_brk can be called only with TBROK or TCONF ($res)" + else + tst_res "$res" "$@" + fi + _tst_do_exit } @@ -139,7 +147,7 @@ ROD_SILENT() { local tst_out - tst_out="$(tst_rod $@ 2>&1)" + tst_out=$(tst_rod "$@" 2>&1) if [ $? -ne 0 ]; then echo "$tst_out" tst_brk TBROK "$@ failed" @@ -474,15 +482,16 @@ tst_usage() Environment Variables --------------------- -KCONFIG_PATH Specify kernel config file -KCONFIG_SKIP_CHECK Skip kernel config check if variable set (not set by default) -LTPROOT Prefix for installed LTP (default: /opt/ltp) -LTP_COLORIZE_OUTPUT Force colorized output behaviour (y/1 always, n/0: never) -LTP_DEV Path to the block device to be used (for .needs_device) -LTP_DEV_FS_TYPE Filesystem used for testing (default: ext2) -LTP_SINGLE_FS_TYPE Testing only - specifies filesystem instead all supported (for TST_ALL_FILESYSTEMS=1) -LTP_TIMEOUT_MUL Timeout multiplier (must be a number >=1, ceiled to int) -TMPDIR Base directory for template directory (for .needs_tmpdir, default: /tmp) +KCONFIG_PATH Specify kernel config file +KCONFIG_SKIP_CHECK Skip kernel config check if variable set (not set by default) +LTPROOT Prefix for installed LTP (default: /opt/ltp) +LTP_COLORIZE_OUTPUT Force colorized output behaviour (y/1 always, n/0: never) +LTP_DEV Path to the block device to be used (for .needs_device) +LTP_DEV_FS_TYPE Filesystem used for testing (default: ext2) +LTP_SINGLE_FS_TYPE Specifies filesystem instead all supported (for TST_ALL_FILESYSTEMS=1) +LTP_FORCE_SINGLE_FS_TYPE Testing only. The same as LTP_SINGLE_FS_TYPE but ignores test skiplist +LTP_TIMEOUT_MUL Timeout multiplier (must be a number >=1, ceiled to int) +TMPDIR Base directory for template directory (for .needs_tmpdir, default: /tmp) EOF } @@ -618,6 +627,7 @@ _tst_init_checkpoints() tst_brk TBROK "tst_getconf PAGESIZE failed" fi ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$pagesize" count=1 + ROD_SILENT "printf LTPM | dd of="$LTP_IPC_PATH" bs=1 seek=0 conv=notrunc" ROD_SILENT chmod 600 "$LTP_IPC_PATH" export LTP_IPC_PATH } @@ -679,6 +689,7 @@ tst_run() CHECKPOINT_WAKE2|CHECKPOINT_WAKE_AND_WAIT);; DEV_EXTRA_OPTS|DEV_FS_OPTS|FORMAT_DEVICE|MOUNT_DEVICE);; SKIP_FILESYSTEMS|SKIP_IN_LOCKDOWN|SKIP_IN_SECUREBOOT);; + DEVICE_SIZE);; *) tst_res TWARN "Reserved variable TST_$_tst_i used!";; esac done @@ -737,11 +748,12 @@ tst_run() TST_STARTWD=$(pwd) cd "$TST_TMPDIR" + tst_res TINFO "Using $TST_TMPDIR as tmpdir ($(stat -f -c '%T' $TST_TMPDIR) filesystem)" fi # needs to be after cd $TST_TMPDIR to keep test_dev.img under $TST_TMPDIR if [ "$TST_NEEDS_DEVICE" = 1 ]; then - TST_DEVICE=$(tst_device acquire) + TST_DEVICE=$(tst_device acquire $TST_DEVICE_SIZE) if [ ! -b "$TST_DEVICE" -o $? -ne 0 ]; then unset TST_DEVICE @@ -898,6 +910,9 @@ if [ -z "$TST_NO_DEFAULT_RUN" ]; then TST_ARGS="$@" + tst_res TINFO "Running: $(basename $0) $TST_ARGS" + tst_res TINFO "Tested kernel: $(uname -a)" + OPTIND=1 while getopts ":hi:$TST_OPTS" _tst_name $TST_ARGS; do diff --git a/testcases/misc/crash/crash02.c b/testcases/misc/crash/crash02.c index c68f580e..04f56c61 100755 --- a/testcases/misc/crash/crash02.c +++ b/testcases/misc/crash/crash02.c @@ -1,497 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * crash02.c - Test OS robustness by executing syscalls with random args. - * - * Copyright (C) 2001 Stephane Fillod - * - * This test program was inspired from crashme, by GEORGE J. CARRETT. - * - * 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. + * Copyright (C) 2025 SUSE LLC */ -/* -A signal handler is set up so that in most cases the machine exception -generated by the illegal syscall, bad operands, etc in the procedure -made up of random data are caught; and another round of randomness may -be tried. Eventually a random syscall may corrupt the program or -the machine state in such a way that the program must halt. This is -a test of the robustness of the hardware/software for instruction -fault handling. +/*\ + * Test the robustness of the system generating random syscalls execution + * with random data and expecting that the current system is not crashing. + */ -Note: Running this program just a few times, using total CPU time of -less than a few seconds SHOULD NOT GIVE YOU ANY CONFIDENCE in system -robustness. Having it run for hours, with tens of thousands of cases -would be a different thing. It would also make sense to run this -stress test at the same time you run other tests, like a multi-user -benchmark. - -CAUTION: running this program may crash your system, your disk and all - your data along! DO NOT RUN IT ON PRODUCTION SYSTEMS! - CONSIDER YOUR DISK FRIED. - REMEMBER THE DISCLAIMER PART OF THE LICENSE. - - Running as user nobody and with all your filesystems - remounted to readonly may be wise.. - -TODO: - * in rand_long(), stuff in some real pointers to random data - * Does a syscall is supposed to send SIGSEGV? -*/ - -#define _GNU_SOURCE -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include "tst_test.h" +#include "lapi/syscalls.h" -#include "test.h" +#define MAX_SYSCALLS 465 -char *TCID = "crash02"; -int TST_TOTAL = 1; +static int *num_errors; +static char *str_num_tries; +static char *str_seed; +static int num_tries = 1000; +static int seed; -static int x_opt = 0; -static int v_opt = 0; -static char *v_copt; -static int s_opt = 0; -static char *s_copt; -static int l_opt = 0; -static char *l_copt; -static int n_opt = 0; -static char *n_copt; - -int verbose_level = 2; - -/* depends on architecture.. */ -unsigned int sysno_max = 127; - -int nseed; -int ntries = 100; - -/* max time allowed per try, in seconds */ -#define MAX_TRY_TIME 5 - -void cleanup(void) -{ - - tst_rmdir(); - -} - -void setup(void) -{ - /* - * setup a default signal hander and a - * temporary working directory. - */ - tst_sig(FORK, DEF_HANDLER, cleanup); - - TEST_PAUSE; - - tst_tmpdir(); -} - -void help(void) -{ - printf - (" -x dry run, hexdump random code instead\n"); - printf(" -l x max syscall no\n"); - printf(" -v x verbose level\n"); - printf(" -s x random seed\n"); - printf(" -n x ntries\n"); -} - -/* - */ -option_t options[] = { - {"v:", &v_opt, &v_copt}, - {"l:", &l_opt, &l_copt}, - {"s:", &s_opt, &s_copt}, - {"n:", &n_opt, &n_copt}, - {"x", &x_opt, NULL}, - - {NULL, NULL, NULL} +static int blacklist[] = { + __NR_vfork, + __NR_fork, + __NR_clone, + __NR_clone2, + __NR_clone3, + __NR_vhangup, /* terminal logout */ + __NR_pause, /* sleep indefinitely */ + __NR_read, /* sleep indefinitely if the first argument is 0 */ + __NR_kill, /* might kill test */ + __NR_restart_syscall, /* restart random syscalls */ + __LTP__NR_INVALID_SYSCALL, }; -void badboy_fork(); -void badboy_loop(); - -void summarize_errno(); -void record_errno(unsigned int n); - -int main(int argc, char *argv[]) +static long rand_number(void) { - int lc; + long num = 0; - tst_parse_opts(argc, argv, options, help); + for (size_t i = 0; i < sizeof(long); i++) + num |= ((rand() & 0xFFUL) << (i * 8)); - if (v_opt) - verbose_level = atoi(v_copt); - - if (n_opt) - ntries = atoi(n_copt); - - if (l_opt) - sysno_max = atoi(l_copt); - - if (s_opt) - nseed = atoi(s_copt); - else - nseed = time(NULL); - - setup(); - - for (lc = 0; TEST_LOOPING(lc); lc++) { - tst_count = 0; - - tst_resm(TINFO, "crashme02 %d %d %d", sysno_max, nseed, ntries); - - srand(nseed); - badboy_fork(); - - /* still there? */ - tst_resm(TPASS, "we're still here, OS seems to be robust"); - - nseed++; - } - cleanup(); - tst_exit(); + return num; } -/* ************************* */ -int badboy_pid; - -void my_signal(int sig, void (*func) ()); - -void monitor_fcn(int sig) +static int in_blacklist(const int sysno) { - int status; - - if (verbose_level >= 3) - printf("time limit reached on pid. using kill.\n"); - - status = kill(badboy_pid, SIGKILL); - if (status < 0) { - if (verbose_level >= 3) - printf("failed to kill process\n"); - } -} - -void badboy_fork(void) -{ - int status, pid; - pid_t child = fork(); - - switch (child) { - case -1: - perror("fork"); - case 0: -#ifdef DEBUG_LATE_BADBOY - sleep(ntries * MAX_TRY_TIME + 10); -#else - badboy_loop(); -#endif - exit(0); - default: - badboy_pid = child; - if (verbose_level > 3) - printf("badboy pid = %d\n", badboy_pid); - - /* don't trust the child to return at night */ - my_signal(SIGALRM, monitor_fcn); - alarm(ntries * MAX_TRY_TIME); - - pid = waitpid(-1, &status, WUNTRACED); - if (pid <= 0) - perror("wait"); - else { - if (verbose_level > 3) - printf("pid %d exited with status %d\n", - pid, status); -#if 0 - record_status(status); -#endif - } - } - alarm(0); -} - -/* *************** status recording ************************* */ - -/* errno status table (max is actually around 127) */ -#define STATUS_MAX 256 -static int errno_table[STATUS_MAX]; - -void record_errno(unsigned int n) -{ - if (n >= STATUS_MAX) - return; - - errno_table[n]++; -} - -/* may not work with -c option */ -void summarize_errno(void) -{ - int i; - - if (x_opt || verbose_level < 2) - return; - - printf("errno status ... number of cases\n"); - for (i = 0; i < STATUS_MAX; i++) { - if (errno_table[i]) - printf("%12d ... %5d\n", i, errno_table[i]); - } -} - -/* ************* badboy ******************************************* */ - -jmp_buf again_buff; - -unsigned char *bad_malloc(int n); -void my_signal(int sig, void (*func) ()); -void again_handler(int sig); -void try_one_crash(int try_num); -void set_up_signals(); -int in_blacklist(int sysno); - -/* badboy "entry" point */ - -/* - * Unlike crashme, faulty syscalls are not supposed to barf - */ -void badboy_loop(void) -{ - int i; - - for (i = 0; i < ntries; ++i) { - /* level 5 */ - - if (!x_opt && verbose_level >= 5) { - printf("try %d\n", i); - } - - if (setjmp(again_buff) == 3) { - if (verbose_level >= 5) - printf("Barfed\n"); - } else { - set_up_signals(); - alarm(MAX_TRY_TIME); - try_one_crash(i); - } - } - summarize_errno(); -} - -void again_handler(int sig) -{ - char *ss; - - switch (sig) { - case SIGILL: - ss = " illegal instruction"; - break; -#ifdef SIGTRAP - case SIGTRAP: - ss = " trace trap"; - break; -#endif - case SIGFPE: - ss = " arithmetic exception"; - break; -#ifdef SIGBUS - case SIGBUS: - ss = " bus error"; - break; -#endif - case SIGSEGV: - ss = " segmentation violation"; - break; -#ifdef SIGIOT - case SIGIOT: - ss = " IOT instruction"; - break; -#endif -#ifdef SIGEMT - case SIGEMT: - ss = " EMT instruction"; - break; -#endif -#ifdef SIGALRM - case SIGALRM: - ss = " alarm clock"; - break; -#endif - case SIGINT: - ss = " interrupt"; - break; - default: - ss = ""; - } - if (verbose_level >= 5) - printf("Got signal %d%s\n", sig, ss); - - longjmp(again_buff, 3); -} - -void my_signal(int sig, void (*func) ()) -{ - struct sigaction act; - - act.sa_handler = func; - memset(&act.sa_mask, 0x00, sizeof(sigset_t)); - act.sa_flags = SA_NOMASK | SA_RESTART; - sigaction(sig, &act, 0); -} - -void set_up_signals(void) -{ - my_signal(SIGILL, again_handler); -#ifdef SIGTRAP - my_signal(SIGTRAP, again_handler); -#endif - my_signal(SIGFPE, again_handler); -#ifdef SIGBUS - my_signal(SIGBUS, again_handler); -#endif - my_signal(SIGSEGV, again_handler); -#ifdef SIGIOT - my_signal(SIGIOT, again_handler); -#endif -#ifdef SIGEMT - my_signal(SIGEMT, again_handler); -#endif -#ifdef SIGALRM - my_signal(SIGALRM, again_handler); -#endif - my_signal(SIGINT, again_handler); -} - -/* - * NB: rand() (ie. RAND_MAX) might be on 31bits only! - * - * FIXME: 64-bit systems - * - * TODO: improve arg mixing (16bits and 8bits values, NULLs, etc.). - * big values as returned by rand() are no so interresting - * (except when used as pointers) because they may fall too - * quickly in the invalid parameter sieve. Smaller values, - * will be more insidious because they may refer to existing - * objects (pids, fd, etc.). - */ -long int rand_long(void) -{ - int r1, r2; - - r1 = rand(); - r2 = rand(); - - if (r1 & 0x10000L) - r1 = 0; - if (!r1 && (r2 & 0x50000L)) - r2 = 0; - else if (!r1 && (r2 & 0x20000L)) - r2 &= 0x00ffL; - - return (long int)((r1 & 0xffffL) << 16) | (r2 & 0xffffL); -} - -void try_one_crash(int try_num) -{ - long int sysno, arg1, arg2, arg3, arg4, arg5, arg6, arg7; - - do { - sysno = rand() % sysno_max; - } while (in_blacklist(sysno)); - - arg1 = rand_long(); - arg2 = rand_long(); - arg3 = rand_long(); - arg4 = rand_long(); - arg5 = rand_long(); - arg6 = rand_long(); - arg7 = rand_long(); - - if (x_opt || verbose_level >= 1) - printf("%04d: syscall(%ld, %#lx, %#lx, %#lx, %#lx, %#lx, " - "%#lx, %#lx)\n", try_num, sysno, arg1, arg2, arg3, - arg4, arg5, arg6, arg7); - - if (!x_opt) { - syscall(sysno, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - record_errno(errno); - } -} - -/* The following syscalls create new processes which may cause the test - unable to finish. */ -int in_blacklist(int sysno) -{ - int i; - const int list[] = { -#if defined(__ia64__) - SYS_clone2, -#else - /* - * No SYS_fork(vfork) on IA-64. Instead, it uses, - * clone(child_stack=0, flags=CLONE_VM|CLONE_VFORK|SIGCHLD) - * clone2() - */ - - /* - * NOTE (garrcoop): - * Could not find reference to SYS_fork(vfork) on mips32 - * with the Montavista / Octeon toolchain. Need to develop an - * autoconf check for this item. - */ -#if defined(__NR_vfork) && __NR_vfork - SYS_vfork, -#endif -#if defined(__NR_fork) && __NR_fork - SYS_fork, -#endif -#endif /* __ia64__ */ -#if defined(__NR_clone) && __NR_clone - SYS_clone, -#endif -#if defined(__NR_vhangup) && __NR_vhangup - __NR_vhangup, /* int vhangup(void); - terminal logout */ -#endif -#if defined(__NR_pause) && __NR_pause - __NR_pause, /* int pause(void); - sleep indefinitely */ -#endif -#if defined(__NR_read) && __NR_read - /* - * ssize_t read(int fd, void *buf, size_t count); - will sleep - * indefinitely if the first argument is 0 - */ - __NR_read, -#endif - -1 - }; - - for (i = 0; list[i] != -1; i++) { - if (sysno == list[i]) + for (size_t i = 0; i < ARRAY_SIZE(blacklist); i++) + if (sysno == blacklist[i]) return 1; - } return 0; } + +static void try_crash(const int num) +{ + long sysno, arg0, arg1, arg2, arg3, arg4, arg5, arg6; + int ret; + + do { + sysno = rand() % MAX_SYSCALLS; + } while (in_blacklist(sysno)); + + arg0 = rand_number(); + arg1 = rand_number(); + arg2 = rand_number(); + arg3 = rand_number(); + arg4 = rand_number(); + arg5 = rand_number(); + arg6 = rand_number(); + + tst_res(TDEBUG, + "%d: syscall(%ld, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx)", + num, sysno, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + + ret = syscall(sysno, arg0, arg1, arg2, arg3, arg4, arg5, arg6); + if (ret == -1) { + (*num_errors)++; + + tst_res(TDEBUG, "syscall error: %s", strerror(errno)); + } +} + +static void run(void) +{ + pid_t pid; + int status; + int num_signals = 0; + + *num_errors = 0; + + pid = SAFE_FORK(); + if (!pid) { + for (int i = 0; i < num_tries; i++) + try_crash(i); + + exit(0); + } + + SAFE_WAITPID(pid, &status, 0); + + if (WIFSIGNALED(status)) { + num_signals++; + + tst_res(TDEBUG, "syscall signaled: %s", + strsignal(WTERMSIG(status))); + } + + tst_res(TINFO, "Detected errors: %d", *num_errors); + tst_res(TINFO, "Detected signals: %d", num_signals); + + tst_res(TPASS, "System is still up and running"); +} + +static void setup(void) +{ + if (tst_parse_int(str_num_tries, &num_tries, 1, INT_MAX)) + tst_brk(TBROK, "Invalid number of entries '%s'", str_num_tries); + + if (tst_parse_int(str_seed, &seed, 0, INT_MAX)) + tst_brk(TBROK, "Invalid seed number '%s'", str_num_tries); + + num_errors = SAFE_MMAP( + NULL, sizeof(int), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, + -1, 0); + + seed = str_seed ? seed : time(NULL); + srand(seed); + + tst_res(TINFO, "Random seed: %d", seed); + tst_res(TINFO, "Number of tries: %d", num_tries); + + tst_set_runtime((num_tries / 1000) + 1); +} + +static void cleanup(void) +{ + if (num_errors) + SAFE_MUNMAP(num_errors, sizeof(int)); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .forks_child = 1, + .options = (struct tst_option []) { + {"n:", &str_num_tries, "Number of retries (default: 1000)"}, + {"s:", &str_seed, "Initial seed for random generator"}, + {} + }, +}; diff --git a/testcases/misc/lvm/datafiles/runfile.tpl b/testcases/misc/lvm/datafiles/runfile.tpl index 4c80e939..ee733626 100755 --- a/testcases/misc/lvm/datafiles/runfile.tpl +++ b/testcases/misc/lvm/datafiles/runfile.tpl @@ -29,6 +29,10 @@ {fsname}_gf28 growfiles -W {fsname}_gf28 -b -D 0 -w -g 16b -C 1 -b -i 1000 -u {tempdir}/{fsname}/gfsparse-2-$$ {fsname}_gf29 growfiles -W {fsname}_gf29 -b -D 0 -r 1-4096 -R 0-33554432 -i 0 -L 60 -B 805306368 -C 1 -u {tempdir}/{fsname}/gfsparse-3-$$ {fsname}_gf30 growfiles -W {fsname}_gf30 -D 0 -b -i 0 -L 60 -u -B 1000b -e 1 -o O_RDWR,O_CREAT,O_SYNC -g 20480 -T 10 -t 20480 {tempdir}/{fsname}/gf-sync-$$ +{fsname}_plough01 fsplough -d {tempdir}/{fsname} +{fsname}_plough02 fsplough -R -d {tempdir}/{fsname} +{fsname}_plough03 fsplough -W -d {tempdir}/{fsname} +{fsname}_plough04 fsplough -RW -d {tempdir}/{fsname} {fsname}_rwtest01 rwtest -N {fsname}_rwtest01 -c -q -i 60s -f sync 10%25000:rw-sync-$$ 500b:{tempdir}/{fsname}/rwtest01%f {fsname}_rwtest02 rwtest -N {fsname}_rwtest02 -c -q -i 60s -f buffered 10%25000:rw-buffered-$$ 500b:{tempdir}/{fsname}/rwtest02%f {fsname}_rwtest03 rwtest -N {fsname}_rwtest03 -c -q -i 60s -n 2 -f buffered -s mmread,mmwrite -m random -Dv 10%25000:mm-buff-$$ 500b:{tempdir}/{fsname}/rwtest03%f diff --git a/testcases/network/Makefile b/testcases/network/Makefile index ccc90839..05d7f95f 100755 --- a/testcases/network/Makefile +++ b/testcases/network/Makefile @@ -14,7 +14,6 @@ CLEAN_TARGETS += $(DIR) INSTALL_DIR := testcases/bin INSTALL_TARGETS := $(addprefix $(DIR)/ascii.,sm med lg jmb) -INSTALL_TARGETS += $(addprefix $(DIR)/bin.,sm med lg jmb) RM += -r diff --git a/testcases/network/README.md b/testcases/network/README.md index a0a1d3d9..0b64df01 100755 --- a/testcases/network/README.md +++ b/testcases/network/README.md @@ -84,6 +84,17 @@ Where Default values for all LTP network parameters are set in `testcases/lib/tst_net.sh`. Network stress parameters are documented in `testcases/network/stress/README`. +Tests which use `tst_netload_compare()` test also performance. They can fail on +overloaded SUT. To ignore performance failure and test only the network functionality, +set `LTP_NET_FEATURES_IGNORE_PERFORMANCE_FAILURE=1` environment variable. + +## Dependencies + +Some network tests which use `tst_ping()` function require `ping` with `-I` support, +which is not supported by `ping` from [inetutils](https://www.gnu.org/software/inetutils/). +Use `ping` from [iputils](https://github.com/iputils/iputils/) or from [BusyBox](https://busybox.net/) +(configured with `CONFIG_FEATURE_IPV6=y` `CONFIG_FEATURE_FANCY_PING=y`). + ## Debugging Both single and two host configurations support debugging via `TST_NET_RHOST_RUN_DEBUG=1` environment variable. diff --git a/testcases/network/busy_poll/busy_poll01.sh b/testcases/network/busy_poll/busy_poll01.sh index 65f4db3f..620084fc 100755 --- a/testcases/network/busy_poll/busy_poll01.sh +++ b/testcases/network/busy_poll/busy_poll01.sh @@ -39,7 +39,7 @@ test() for x in 50 0; do tst_res TINFO "set low latency busy poll to $x" set_busy_poll $x - tst_netload -H $(tst_ipaddr rhost) -n 10 -N 10 -d res_$x + tst_netload -H $(tst_ipaddr rhost) -n 10 -N 10 -c res_$x done tst_netload_compare $(cat res_0) $(cat res_50) 1 diff --git a/testcases/network/busy_poll/busy_poll02.sh b/testcases/network/busy_poll/busy_poll02.sh index ebae4d2f..8ba701cc 100755 --- a/testcases/network/busy_poll/busy_poll02.sh +++ b/testcases/network/busy_poll/busy_poll02.sh @@ -31,7 +31,7 @@ test() for x in 50 0; do tst_res TINFO "set low latency busy poll to $x per socket" set_busy_poll $x - tst_netload -H $(tst_ipaddr rhost) -n 10 -N 10 -d res_$x -b $x + tst_netload -H $(tst_ipaddr rhost) -n 10 -N 10 -c res_$x -b $x done tst_netload_compare $(cat res_0) $(cat res_50) 1 diff --git a/testcases/network/busy_poll/busy_poll03.sh b/testcases/network/busy_poll/busy_poll03.sh index 04d5978f..d35dde6e 100755 --- a/testcases/network/busy_poll/busy_poll03.sh +++ b/testcases/network/busy_poll/busy_poll03.sh @@ -33,7 +33,7 @@ test() for x in 50 0; do tst_res TINFO "set low latency busy poll to $x per $2 socket" set_busy_poll $x - tst_netload -H $(tst_ipaddr rhost) -n 10 -N 10 -d res_$x \ + tst_netload -H $(tst_ipaddr rhost) -n 10 -N 10 -c res_$x \ -b $x -T $2 done diff --git a/testcases/network/busy_poll/busy_poll_lib.sh b/testcases/network/busy_poll/busy_poll_lib.sh index de61d3fc..dd88dd9c 100755 --- a/testcases/network/busy_poll/busy_poll_lib.sh +++ b/testcases/network/busy_poll/busy_poll_lib.sh @@ -5,7 +5,6 @@ TST_SETUP="setup" TST_TESTFUNC="test" TST_CLEANUP="cleanup" -TST_MIN_KVER="3.11" TST_NEEDS_TMPDIR=1 TST_NEEDS_ROOT=1 TST_NEEDS_CMDS="pkill sysctl ethtool" diff --git a/testcases/network/can/cve/can_bcm01.c b/testcases/network/can/cve/can_bcm01.c index 79a827cf..57ec4989 100755 --- a/testcases/network/can/cve/can_bcm01.c +++ b/testcases/network/can/cve/can_bcm01.c @@ -1,16 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2020 SUSE LLC - * + */ + +/*\ * CVE-2021-3609 * * Test for race condition vulnerability in CAN BCM. Fixed in: - * - * commit d5f9023fa61ee8b94f37a93f08e94b136cf1e463 - * Author: Thadeu Lima de Souza Cascardo - * Date: Sat Jun 19 13:18:13 2021 -0300 - * - * can: bcm: delay release of struct bcm_op after synchronize_rcu() + * d5f9023fa61e ("can: bcm: delay release of struct bcm_op after synchronize_rcu()"). * * The test is skipped when running in 32-bit compat mode. The kernel * compatibility layer for CAN structures is not implemented at the @@ -142,7 +139,7 @@ static struct tst_test test = { .taint_check = TST_TAINT_W | TST_TAINT_D, .needs_root = 1, .skip_in_compat = 1, - .max_runtime = 30, + .min_runtime = 30, .needs_drivers = (const char *const[]) { "vcan", "can-bcm", diff --git a/testcases/network/can/filter-tests/can_filter.c b/testcases/network/can/filter-tests/can_filter.c index 19c7fc48..1ea8ea18 100755 --- a/testcases/network/can/filter-tests/can_filter.c +++ b/testcases/network/can/filter-tests/can_filter.c @@ -172,7 +172,7 @@ static void run(unsigned int n) static struct tst_test test = { .tcnt = TC, .options = (struct tst_option[]) { - {"D:", &can_dev_name, "CAN device name"}, + {"d:", &can_dev_name, "CAN device name"}, {} }, .setup = setup, diff --git a/testcases/network/can/filter-tests/can_rcv_own_msgs.c b/testcases/network/can/filter-tests/can_rcv_own_msgs.c index 6bb2619f..ba40666d 100755 --- a/testcases/network/can/filter-tests/can_rcv_own_msgs.c +++ b/testcases/network/can/filter-tests/can_rcv_own_msgs.c @@ -134,7 +134,7 @@ static void run(void) static struct tst_test test = { .options = (struct tst_option[]) { - {"D:", &can_dev_name, "CAN device name"}, + {"d:", &can_dev_name, "CAN device name"}, {} }, .setup = setup, diff --git a/testcases/network/dhcp/dnsmasq_tests.sh b/testcases/network/dhcp/dnsmasq_tests.sh index 0183c1da..c0b7686a 100755 --- a/testcases/network/dhcp/dnsmasq_tests.sh +++ b/testcases/network/dhcp/dnsmasq_tests.sh @@ -33,12 +33,12 @@ print_dhcp_version() dnsmasq --version | head -2 } +dhcp_name="dnsmasq" . dhcp_lib.sh lease_dir="/var/lib/misc" tst_selinux_enforced && lease_dir="/var/lib/dnsmasq" -dhcp_name="dnsmasq" log="/var/log/dnsmasq.tst.log" lease_file="$lease_dir/dnsmasq.tst.leases" diff --git a/testcases/network/generate.sh b/testcases/network/generate.sh index 939f792c..af48f773 100755 --- a/testcases/network/generate.sh +++ b/testcases/network/generate.sh @@ -51,29 +51,3 @@ fi "$make_file" "$medium_file" $medium_size "$make_file" "$large_file" $large_size "$make_file" "$jumbo_file" $jumbo_size - -if [ ! -e "bin.sm" ] ; then - cnt=0 - while [ $cnt -lt 5 ] ; do - gzip -1 -c -n ascii.sm >> "bin.sm" - cnt=$(($cnt + 1)) - done -fi - -genfile() { - local input="$1" output="$2" - local cnt=19 - - [ -e "$output" ] && return $? - - while [ $cnt -ge 0 ] ; do - cat "$input" >> "$output" - cnt=$(($cnt-1)) - done -} - -genfile bin.sm bin.med -genfile bin.med bin.lg -genfile bin.lg bin.jmb - -chmod 666 bin.* diff --git a/testcases/network/iproute/ip_tests.sh b/testcases/network/iproute/ip_tests.sh index ee976807..77940481 100755 --- a/testcases/network/iproute/ip_tests.sh +++ b/testcases/network/iproute/ip_tests.sh @@ -12,7 +12,7 @@ TST_TESTFUNC="test" TST_CLEANUP="cleanup" TST_NEEDS_TMPDIR=1 TST_NEEDS_ROOT=1 -TST_NEEDS_CMDS="cat awk diff" +TST_NEEDS_CMDS="awk cat diff ip lsmod modprobe" TST_NEEDS_DRIVERS="dummy" @@ -179,7 +179,7 @@ test5() $ip4_addr via 127.0.0.1 dev lo EOF - ip route show | grep "$ip4_addr via 127.0.0.1 dev lo" > tst_ip.out 2>&1 + ip route show | grep "$ip4_addr via 127\.0\.0\.1 dev lo" > tst_ip\.out 2>&1 if [ $? -ne 0 ]; then tst_res TFAIL "'ip route show' command failed" return @@ -195,7 +195,7 @@ $ip4_addr via 127.0.0.1 dev lo ROD ip route del $ip4_addr via 127.0.0.1 - ip route show | grep 127.0.0.1 > /dev/null + ip route show | grep -q "$ip4_addr via 127\.0\.0\.1 dev lo" if [ $? -eq 0 ]; then tst_res TFAIL "route not deleted" return diff --git a/testcases/network/iptables/.gitignore b/testcases/network/iptables/.gitignore new file mode 100644 index 00000000..0f47a731 --- /dev/null +++ b/testcases/network/iptables/.gitignore @@ -0,0 +1 @@ +nft02 diff --git a/testcases/network/iptables/Makefile b/testcases/network/iptables/Makefile index 1b42f25d..02e228cb 100755 --- a/testcases/network/iptables/Makefile +++ b/testcases/network/iptables/Makefile @@ -5,7 +5,7 @@ top_srcdir ?= ../../.. -include $(top_srcdir)/include/mk/env_pre.mk +include $(top_srcdir)/include/mk/testcases.mk INSTALL_TARGETS := *.sh diff --git a/testcases/network/iptables/nft02.c b/testcases/network/iptables/nft02.c new file mode 100644 index 00000000..5f0671c5 --- /dev/null +++ b/testcases/network/iptables/nft02.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 SUSE LLC + * Author: Marcos Paulo de Souza + * LTP port: Martin Doucha + */ + +/*\ + * CVE-2023-31248 + * + * Test for use-after-free when adding a new rule to a chain deleted + * in the same netlink message batch. + * + * Kernel bug fixed in: + * + * commit 515ad530795c118f012539ed76d02bacfd426d89 + * Author: Thadeu Lima de Souza Cascardo + * Date: Wed Jul 5 09:12:55 2023 -0300 + * + * netfilter: nf_tables: do not ignore genmask when looking up chain by id + */ + +#include +#include +#include +#include +#include "lapi/nf_tables.h" +#include +#include "tst_test.h" +#include "tst_netlink.h" + +#define TABNAME "ltp_table1" +#define SRCCHAIN "ltp_chain_src" +#define DESTCHAIN "ltp_chain_dest" +#define DESTCHAIN_ID 77 + +static uint32_t chain_id; +static uint32_t imm_dreg, imm_verdict; +static struct tst_netlink_context *ctx; + +/* Table creation config */ +static const struct tst_netlink_attr_list table_config[] = { + {NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL}, + {0, NULL, -1, NULL} +}; + +/* Chain creation and deletion config */ +static const struct tst_netlink_attr_list destchain_config[] = { + {NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL}, + {NFTA_CHAIN_NAME, DESTCHAIN, strlen(DESTCHAIN) + 1, NULL}, + {NFTA_CHAIN_ID, &chain_id, sizeof(chain_id), NULL}, + {0, NULL, -1, NULL} +}; + +static const struct tst_netlink_attr_list delchain_config[] = { + {NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL}, + {NFTA_CHAIN_NAME, DESTCHAIN, strlen(DESTCHAIN) + 1, NULL}, + {0, NULL, -1, NULL} +}; + +static const struct tst_netlink_attr_list srcchain_config[] = { + {NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL}, + {NFTA_CHAIN_NAME, SRCCHAIN, strlen(SRCCHAIN) + 1, NULL}, + {0, NULL, -1, NULL} +}; + +/* Rule creation config */ +static const struct tst_netlink_attr_list rule_verdict_config[] = { + {NFTA_VERDICT_CODE, &imm_verdict, sizeof(imm_verdict), NULL}, + {NFTA_VERDICT_CHAIN_ID, &chain_id, sizeof(chain_id), NULL}, + {0, NULL, -1, NULL} +}; + +static const struct tst_netlink_attr_list rule_data_config[] = { + {NFTA_IMMEDIATE_DREG, &imm_dreg, sizeof(imm_dreg), NULL}, + {NFTA_IMMEDIATE_DATA, NULL, 0, (const struct tst_netlink_attr_list[]) { + {NFTA_DATA_VERDICT, NULL, 0, rule_verdict_config}, + {0, NULL, -1, NULL} + }}, + {0, NULL, -1, NULL} +}; + +static const struct tst_netlink_attr_list rule_expr_config[] = { + {NFTA_LIST_ELEM, NULL, 0, (const struct tst_netlink_attr_list[]) { + {NFTA_EXPR_NAME, "immediate", 10, NULL}, + {NFTA_EXPR_DATA, NULL, 0, rule_data_config}, + {0, NULL, -1, NULL} + }}, + {0, NULL, -1, NULL} +}; + +static const struct tst_netlink_attr_list rule_config[] = { + {NFTA_RULE_EXPRESSIONS, NULL, 0, rule_expr_config}, + {NFTA_RULE_TABLE, TABNAME, strlen(TABNAME) + 1, NULL}, + {NFTA_RULE_CHAIN, SRCCHAIN, strlen(SRCCHAIN) + 1, NULL}, + {0, NULL, -1, NULL} +}; + +static void setup(void) +{ + tst_setup_netns(); + + chain_id = htonl(DESTCHAIN_ID); + imm_dreg = htonl(NFT_REG_VERDICT); + imm_verdict = htonl(NFT_GOTO); +} + +static void run(void) +{ + int ret; + struct nlmsghdr header; + struct nfgenmsg nfpayload; + + memset(&header, 0, sizeof(header)); + memset(&nfpayload, 0, sizeof(nfpayload)); + nfpayload.version = NFNETLINK_V0; + + ctx = NETLINK_CREATE_CONTEXT(NETLINK_NETFILTER); + + /* Start netfilter batch */ + header.nlmsg_type = NFNL_MSG_BATCH_BEGIN; + header.nlmsg_flags = NLM_F_REQUEST; + nfpayload.nfgen_family = AF_UNSPEC; + nfpayload.res_id = htons(NFNL_SUBSYS_NFTABLES); + NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload)); + + /* Add table */ + header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWTABLE; + header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; + nfpayload.nfgen_family = NFPROTO_IPV4; + nfpayload.res_id = htons(0); + NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload)); + NETLINK_ADD_ATTR_LIST(ctx, table_config); + + /* Add destination chain */ + header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWCHAIN; + header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; + nfpayload.nfgen_family = NFPROTO_IPV4; + nfpayload.res_id = htons(0); + NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload)); + NETLINK_ADD_ATTR_LIST(ctx, destchain_config); + + /* Delete destination chain */ + header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_DELCHAIN; + header.nlmsg_flags = NLM_F_REQUEST; + nfpayload.nfgen_family = NFPROTO_IPV4; + nfpayload.res_id = htons(0); + NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload)); + NETLINK_ADD_ATTR_LIST(ctx, delchain_config); + + /* Add source chain */ + header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWCHAIN; + header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; + nfpayload.nfgen_family = NFPROTO_IPV4; + nfpayload.res_id = htons(0); + NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload)); + NETLINK_ADD_ATTR_LIST(ctx, srcchain_config); + + /* Add rule to source chain. Require ACK and check for ENOENT error. */ + header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWRULE; + header.nlmsg_flags = NLM_F_REQUEST | NLM_F_APPEND | NLM_F_CREATE | + NLM_F_ACK; + nfpayload.nfgen_family = NFPROTO_IPV4; + nfpayload.res_id = htons(0); + NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload)); + NETLINK_ADD_ATTR_LIST(ctx, rule_config); + + /* End batch */ + header.nlmsg_type = NFNL_MSG_BATCH_END; + header.nlmsg_flags = NLM_F_REQUEST; + nfpayload.nfgen_family = AF_UNSPEC; + nfpayload.res_id = htons(NFNL_SUBSYS_NFTABLES); + NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload)); + + ret = NETLINK_SEND_VALIDATE(ctx); + TST_ERR = tst_netlink_errno; + NETLINK_DESTROY_CONTEXT(ctx); + ctx = NULL; + + if (ret) + tst_res(TFAIL, "Netfilter chain list is corrupted"); + else if (TST_ERR == ENOENT) + tst_res(TPASS, "Deleted netfilter chain cannot be referenced"); + else if (TST_ERR == EOPNOTSUPP || TST_ERR == EINVAL) + tst_brk(TCONF, "Test requires unavailable netfilter features"); + else + tst_brk(TBROK | TTERRNO, "Unknown nfnetlink error"); +} + +static void cleanup(void) +{ + NETLINK_DESTROY_CONTEXT(ctx); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .taint_check = TST_TAINT_W | TST_TAINT_D, + .needs_kconfigs = (const char *[]) { + "CONFIG_USER_NS=y", + "CONFIG_NF_TABLES", + NULL + }, + .save_restore = (const struct tst_path_val[]) { + {"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP}, + {} + }, + .tags = (const struct tst_tag[]) { + {"linux-git", "515ad530795c"}, + {"CVE", "2023-31248"}, + {} + } +}; diff --git a/testcases/network/lib6/asapi_02.c b/testcases/network/lib6/asapi_02.c index 7808b2c3..6bffde03 100755 --- a/testcases/network/lib6/asapi_02.c +++ b/testcases/network/lib6/asapi_02.c @@ -6,46 +6,45 @@ */ /*\ - * [Description] + * Basic test for ``ICMP6_FILTER``. * - * Basic test for ICMP6_FILTER. - * - * For ICMP6_FILTER usage, refer to: https://man.openbsd.org/icmp6. + * For ``ICMP6_FILTER`` usage, refer to: https://man.openbsd.org/icmp6. * * Because of the extra functionality of ICMPv6 in comparison to ICMPv4, a * larger number of messages may be potentially received on an ICMPv6 socket. * Input filters may therefore be used to restrict input to a subset of the * incoming ICMPv6 messages so only interesting messages are returned by the - * recv(2) family of calls to an application. + * :man2:`recv` family of calls to an application. * The icmp6_filter structure may be used to refine the input message set * according to the ICMPv6 type. By default, all messages types are allowed * on newly created raw ICMPv6 sockets. The following macros may be used to * refine the input set, thus being tested: * - * void ICMP6_FILTER_SETPASSALL(struct icmp6_filter *filterp) - * – Allow all incoming messages. filterp is modified to allow all message types. + * ``void ICMP6_FILTER_SETPASSALL(struct icmp6_filter *filterp)`` + * -- Allow all incoming messages. filterp is modified to allow all message types. * - * void ICMP6_FILTER_SETBLOCKALL(struct icmp6_filter *filterp) - * – Ignore all incoming messages. filterp is modified to ignore all message types. + * ``void ICMP6_FILTER_SETBLOCKALL(struct icmp6_filter *filterp)`` + * -- Ignore all incoming messages. filterp is modified to ignore all message types. * - * void ICMP6_FILTER_SETPASS(int, struct icmp6_filter *filterp) - * – Allow ICMPv6 messages with the given type. filterp is modified to allow such + * ``void ICMP6_FILTER_SETPASS(int, struct icmp6_filter *filterp)`` + * -- Allow ICMPv6 messages with the given type. filterp is modified to allow such * messages. * - * void ICMP6_FILTER_SETBLOCK(int, struct icmp6_filter *filterp) - * – Ignore ICMPv6 messages with the given type. filterp is modified to ignore + * ``void ICMP6_FILTER_SETBLOCK(int, struct icmp6_filter *filterp)`` + * -- Ignore ICMPv6 messages with the given type. filterp is modified to ignore * such messages. * - * int ICMP6_FILTER_WILLPASS(int, const struct icmp6_filter *filterp) - * – Determine if the given filter will allow an ICMPv6 message of the given type. + * ``int ICMP6_FILTER_WILLPASS(int, const struct icmp6_filter *filterp)`` + * -- Determine if the given filter will allow an ICMPv6 message of the given type. * - * int ICMP6_FILTER_WILLBLOCK(int, const struct icmp6_filter *) - * – Determine if the given filter will ignore an ICMPv6 message of the given type. + * ``int ICMP6_FILTER_WILLBLOCK(int, const struct icmp6_filter *)`` + * Determine if the given filter will ignore an ICMPv6 message of the given type. * - * The getsockopt(2) and setsockopt(2) calls may be used to obtain and install - * the filter on ICMPv6 sockets at option level IPPROTO_ICMPV6 and name ICMP6_FILTER - * with a pointer to the icmp6_filter structure as the option value. + * The :man2:`getsockopt` and :man2:`setsockopt` calls may be used to obtain and + * install the filter on ICMPv6 sockets at option level ``IPPROTO_ICMPV6`` and + * name ``ICMP6_FILTER`` with a pointer to the icmp6_filter structure as the + * option value. */ #include diff --git a/testcases/network/lib6/getaddrinfo_01.c b/testcases/network/lib6/getaddrinfo_01.c index 19740214..61a4aad5 100755 --- a/testcases/network/lib6/getaddrinfo_01.c +++ b/testcases/network/lib6/getaddrinfo_01.c @@ -8,8 +8,6 @@ */ /*\ - * [Description] - * * Basic getaddrinfo() tests. * * The test adds LTP specific addresses and names to /etc/hosts to avoid diff --git a/testcases/network/lib6/in6_01.c b/testcases/network/lib6/in6_01.c index 823d3cac..b5066a58 100755 --- a/testcases/network/lib6/in6_01.c +++ b/testcases/network/lib6/in6_01.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * Verify that in6 and sockaddr fields are present. */ diff --git a/testcases/network/lib6/in6_02.c b/testcases/network/lib6/in6_02.c index e9630d70..71bfe718 100755 --- a/testcases/network/lib6/in6_02.c +++ b/testcases/network/lib6/in6_02.c @@ -7,8 +7,6 @@ */ /*\ - * [Description] - * * IPv6 name to index and index to name function tests. */ diff --git a/testcases/network/mpls/mpls01.sh b/testcases/network/mpls/mpls01.sh index 196b5b2f..e453f813 100755 --- a/testcases/network/mpls/mpls01.sh +++ b/testcases/network/mpls/mpls01.sh @@ -21,7 +21,7 @@ cleanup() setup() { - ROD modprobe mpls_router + mpls_setup_driver } test1() @@ -66,5 +66,5 @@ test3() tst_res TPASS "created and removed mpls routes" } -. tst_net.sh +. mpls_lib.sh tst_run diff --git a/testcases/network/mpls/mpls_lib.sh b/testcases/network/mpls/mpls_lib.sh index 380b568b..486389be 100755 --- a/testcases/network/mpls/mpls_lib.sh +++ b/testcases/network/mpls/mpls_lib.sh @@ -33,11 +33,21 @@ mpls_virt_cleanup() mpls_cleanup } +mpls_setup_driver() +{ + local args + + grep -q -w ID_LIKE.*suse /etc/os-release && args='--allow-unsupported' + if [ "$TST_NEEDS_DRIVERS" ]; then + tst_net_run -s "modprobe $args -a $TST_NEEDS_DRIVERS" + fi +} + mpls_setup() { local label="$1" - tst_net_run -s "modprobe -a $TST_NEEDS_DRIVERS" + mpls_setup_driver ROD sysctl -q net.mpls.conf.$(tst_iface).input=1 tst_set_sysctl net.mpls.conf.lo.input 1 safe diff --git a/testcases/network/netstress/netstress.c b/testcases/network/netstress/netstress.c index fb6281cd..36dbf05c 100755 --- a/testcases/network/netstress/netstress.c +++ b/testcases/network/netstress/netstress.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014-2016 Oracle and/or its affiliates. All Rights Reserved. + * Copyright (c) 2014-2023 Petr Vorel * Author: Alexey Kodanev */ @@ -883,9 +884,6 @@ static void setup(void) if (!clients_num) clients_num = sysconf(_SC_NPROCESSORS_ONLN); - if (busy_poll >= 0 && tst_kvercmp(3, 11, 0) < 0) - tst_brk(TCONF, "Test must be run with kernel 3.11 or newer"); - set_protocol_type(); if (client_mode) { @@ -1009,7 +1007,7 @@ static struct tst_test test = { {"T:", &type, "Tcp (default), udp, udp_lite, dccp, sctp"}, {"z", &zcopy, "Enable SO_ZEROCOPY"}, {"P:", &reuse_port, "Enable SO_REUSEPORT"}, - {"D:", &dev, "Bind to device x"}, + {"d:", &dev, "Bind to device x"}, {"H:", &server_addr, "Server name or IP address"}, {"l", &client_mode, "Become client, default is server"}, @@ -1018,7 +1016,7 @@ static struct tst_test test = { {"n:", &narg, "Client message size"}, {"N:", &Narg, "Server message size"}, {"m:", &Targ, "Receive timeout in milliseconds (not used by UDP/DCCP client)"}, - {"d:", &rpath, "Path to file where result is saved"}, + {"c:", &rpath, "Path to file where result is saved"}, {"A:", &Aarg, "Max payload length (generated randomly)"}, {"R:", &Rarg, "Server requests after which conn.closed"}, @@ -1026,6 +1024,6 @@ static struct tst_test test = { {"B:", &server_bg, "Run in background, arg is the process directory"}, {} }, - .max_runtime = 300, + .timeout = 300, .needs_checkpoints = 1, }; diff --git a/testcases/network/nfs/fsx-linux/fsx.sh b/testcases/network/nfs/fsx-linux/fsx.sh index 9bb46ad6..dbecbfaf 100755 --- a/testcases/network/nfs/fsx-linux/fsx.sh +++ b/testcases/network/nfs/fsx-linux/fsx.sh @@ -14,10 +14,9 @@ do_test() { ITERATIONS=${ITERATIONS:=50000} tst_res TINFO "starting fsx-linux -N $ITERATIONS..." - fsx-linux -N $ITERATIONS testfile > fsx-out.log 2>&1 + fsx-linux -N $ITERATIONS if [ "$?" -ne 0 ]; then tst_res TFAIL "Errors have resulted from this test" - cat fsx-out.log else tst_res TPASS "fsx-linux test passed" fi diff --git a/testcases/network/nfs/nfs_stress/nfs02.sh b/testcases/network/nfs/nfs_stress/nfs02.sh index b7fbbce9..b4ec5ecb 100755 --- a/testcases/network/nfs/nfs_stress/nfs02.sh +++ b/testcases/network/nfs/nfs_stress/nfs02.sh @@ -8,7 +8,7 @@ # # Ported by: Robbie Williamson (robbiew@us.ibm.com) -TST_CNT=3 +TST_CNT=4 TST_TESTFUNC="do_test" LTP_DATAFILES="$LTPROOT/testcases/bin/datafiles" @@ -46,5 +46,20 @@ do_test3() tst_res TPASS "test3 passed" } +do_test4() +{ + tst_require_cmds dd diff + + tst_res TINFO "do_test4, copy data in direct mode" + ROD dd oflag=direct if="$LTP_DATAFILES/ascii.jmb" of=ascii2.jmb + echo 3 >/proc/sys/vm/drop_caches + ROD dd iflag=direct if=ascii2.jmb of="$TST_TMPDIR/ascii2.jmb" + echo 3 >/proc/sys/vm/drop_caches + tst_res TINFO "compare both ascii.jmbs" + ROD diff "$LTP_DATAFILES/ascii.jmb" ascii2.jmb + ROD diff "$LTP_DATAFILES/ascii.jmb" "$TST_TMPDIR/ascii2.jmb" + tst_res TPASS "test4 passed" +} + . nfs_lib.sh tst_run diff --git a/testcases/network/nfs/nfs_stress/nfs05_make_tree.c b/testcases/network/nfs/nfs_stress/nfs05_make_tree.c index e2243ac5..0c2df561 100755 --- a/testcases/network/nfs/nfs_stress/nfs05_make_tree.c +++ b/testcases/network/nfs/nfs_stress/nfs05_make_tree.c @@ -46,7 +46,6 @@ #include #include -#include "lapi/mkdirat.h" #include "tst_safe_pthread.h" #include "tst_safe_stdio.h" #include "tst_test.h" @@ -215,5 +214,5 @@ static struct tst_test test = { .options = opts, .test_all = do_test, .setup = setup, - .max_runtime = 300, + .timeout = 300, }; diff --git a/testcases/network/nfs/nfs_stress/nfs06.sh b/testcases/network/nfs/nfs_stress/nfs06.sh index 560df05b..09e55fe3 100755 --- a/testcases/network/nfs/nfs_stress/nfs06.sh +++ b/testcases/network/nfs/nfs_stress/nfs06.sh @@ -34,7 +34,10 @@ do_test() tst_res TINFO "waiting for pids:$pids" for p in $pids; do - wait $p || tst_brk TFAIL "fsstress process failed" + if ! wait $p; then + tst_res TFAIL "fsstress process failed" + return + fi tst_res TINFO "fsstress '$p' completed" done pids= diff --git a/testcases/network/nfs/nfs_stress/nfs09.sh b/testcases/network/nfs/nfs_stress/nfs09.sh new file mode 100644 index 00000000..88c1a1ec --- /dev/null +++ b/testcases/network/nfs/nfs_stress/nfs09.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC +# +# DESCRIPTION: Check that stale NFS client cache does not prevent file +# truncation. +# +# 1. Through NFS, access metadata of a file that does not exist +# 2. Create the file on the remote end (bypassing NFS) +# 3. Through NFS, try to overwrite the file with shorter data + +TST_TESTFUNC="do_test" + +do_test() +{ + local local_file="testfile" + local remote_file="$(nfs_get_remote_path)/$local_file" + local testmsg='File truncated' + local data + + EXPECT_FAIL "ls -l '$local_file'" + tst_rhost_run -c "echo -n 'File rewritten not' >'$remote_file'" + echo -n "$testmsg" >"$local_file" + data=$(tst_rhost_run -c "cat '$remote_file'") + + if [ "$data" != "$testmsg" ]; then + tst_res TFAIL "Wrong file contents, expected '$testmsg', got '$data'" + else + tst_res TPASS "File was truncated correctly" + fi +} + +. nfs_lib.sh +tst_run diff --git a/testcases/network/nfs/nfs_stress/nfs10.sh b/testcases/network/nfs/nfs_stress/nfs10.sh new file mode 100644 index 00000000..e52db8e2 --- /dev/null +++ b/testcases/network/nfs/nfs_stress/nfs10.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024 SUSE LLC +# +# DESCRIPTION: Verify data integrity over NFS, with and without O_DIRECT + +TST_CNT=4 +TST_SETUP="nfs10_setup" +TST_TESTFUNC="do_test" +TST_DEVICE_SIZE=1024 +TST_TIMEOUT=660 + +nfs10_setup() +{ + local bsize=$(stat -f -c %s .) + + if [ -z "$bsize" ] || [ "$bsize" -lt 1024 ]; then + bsize=1024 + fi + + NFS_MOUNT_OPTS="rsize=$bsize,wsize=$bsize" + nfs_setup +} + +do_test1() +{ + tst_res TINFO "Testing buffered write, buffered read" + EXPECT_PASS fsplough -d "$PWD" +} + +do_test2() +{ + tst_res TINFO "Testing buffered write, direct read" + EXPECT_PASS fsplough -R -d "$PWD" +} + +do_test3() +{ + tst_res TINFO "Testing direct write, buffered read" + EXPECT_PASS fsplough -W -d "$PWD" +} + +do_test4() +{ + tst_res TINFO "Testing direct write, direct read" + EXPECT_PASS fsplough -RW -d "$PWD" +} + +. nfs_lib.sh +tst_run diff --git a/testcases/network/nfs/nfs_stress/nfs_lib.sh b/testcases/network/nfs/nfs_stress/nfs_lib.sh index a996f7cc..14425898 100755 --- a/testcases/network/nfs/nfs_stress/nfs_lib.sh +++ b/testcases/network/nfs/nfs_stress/nfs_lib.sh @@ -4,9 +4,9 @@ # Copyright (c) 2015-2018 Oracle and/or its affiliates. All Rights Reserved. # Copyright (c) International Business Machines Corp., 2001 -VERSION=${VERSION:=3} +VERSION=${VERSION:=4.2} NFILES=${NFILES:=1000} -SOCKET_TYPE="${SOCKET_TYPE:-udp}" +SOCKET_TYPE="${SOCKET_TYPE:-tcp}" NFS_TYPE=${NFS_TYPE:=nfs} nfs_usage() @@ -168,6 +168,7 @@ nfs_setup() local local_dir local remote_dir local mount_dir + local nfs_opts if [ "$(stat -f . | grep "Type: nfs")" ]; then tst_brk TCONF "Cannot run nfs-stress test on mounted NFS" @@ -192,8 +193,14 @@ nfs_setup() remote_dir="$(get_remote_dir $i $type)" nfs_setup_server "$remote_dir" "$(($$ + n))" local_dir="$(get_local_dir $i $n)" + nfs_opts="-o proto=$type,vers=$i" + + if [ -n "$NFS_MOUNT_OPTS" ]; then + nfs_opts="$nfs_opts,$NFS_MOUNT_OPTS" + fi + tst_res TINFO "Mounting $local_dir" - nfs_mount "$local_dir" "$remote_dir" "-o proto=$type,vers=$i" + nfs_mount "$local_dir" "$remote_dir" "$nfs_opts" n=$(( n + 1 )) done @@ -214,7 +221,7 @@ nfs_cleanup() local_dir="$(get_local_dir $i $n)" if grep -q "$local_dir" /proc/mounts; then tst_res TINFO "Unmounting $local_dir" - umount $local_dir + umount $local_dir || tst_res TWARN "Unmount failed" fi n=$(( n + 1 )) done diff --git a/testcases/network/nfs/nfslock01/nfs_flock.c b/testcases/network/nfs/nfslock01/nfs_flock.c index 02f105b2..dbe5ffe3 100755 --- a/testcases/network/nfs/nfslock01/nfs_flock.c +++ b/testcases/network/nfs/nfslock01/nfs_flock.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Program for testing file locking. The original data file consists of * characters from 'A' to 'Z'. The data file after running this program * would consist of lines with 1's in even lines and 0's in odd lines. diff --git a/testcases/network/nfs/nfslock01/nfs_flock_dgen.c b/testcases/network/nfs/nfslock01/nfs_flock_dgen.c index 6ecdcdc4..d050e5e5 100755 --- a/testcases/network/nfs/nfslock01/nfs_flock_dgen.c +++ b/testcases/network/nfs/nfslock01/nfs_flock_dgen.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Tool to generate data for testing file locking. * Used in nfslock01.sh. */ diff --git a/testcases/network/nfs/nfslock01/nfslock01.sh b/testcases/network/nfs/nfslock01/nfslock01.sh index 01d59ce8..1f8c2d75 100644 --- a/testcases/network/nfs/nfslock01/nfslock01.sh +++ b/testcases/network/nfs/nfslock01/nfslock01.sh @@ -68,7 +68,8 @@ do_test() for p in $pids; do wait $p if [ $? -ne 0 ]; then - tst_brk TFAIL "nfs_lock process failed" + tst_res TFAIL "nfs_lock process failed" + return else tst_res TINFO "$p completed" fi diff --git a/testcases/network/nfs/nfsstat01/nfsstat01.sh b/testcases/network/nfs/nfsstat01/nfsstat01.sh index 6589c093..3379c4d4 100644 --- a/testcases/network/nfs/nfsstat01/nfsstat01.sh +++ b/testcases/network/nfs/nfsstat01/nfsstat01.sh @@ -3,28 +3,61 @@ # Copyright (c) 2016-2018 Oracle and/or its affiliates. All Rights Reserved. # Copyright (c) International Business Machines Corp., 2001 +TST_SETUP="nfsstat_setup" TST_TESTFUNC="do_test" TST_NEEDS_CMDS="nfsstat" +NS_STAT_RHOST=0 + +nfsstat_setup() +{ + nfs_setup + + if tst_net_use_netns && [ -z "$LTP_NFS_NETNS_USE_LO" ]; then + tst_rhost_run -c "test -r /proc/net/rpc/nfs" && NS_STAT_RHOST=1 + fi +} get_calls() { local name=$1 local field=$2 local nfs_f=$3 - local calls= - local opt= - [ "$name" = "rpc" ] && opt="r" || opt="n" + local netns=${4:-rhost} + local type="lhost" + local calls opt - if tst_net_use_netns || [ "$nfs_f" = "nfs" ]; then - calls="$(grep $name /proc/net/rpc/$nfs_f | cut -d' ' -f$field)" - ROD nfsstat -c$opt | grep -q "$calls" - echo "$calls" - return + [ "$name" = "rpc" ] && opt="r" || opt="n" + [ "$nfs_f" = "nfsd" ] && opt="-s$opt" || opt="-c$opt" + + if tst_net_use_netns; then + # In netns setup, rhost is the client + [ "$nfs_f" = "nfs" ] && [ $NS_STAT_RHOST -ne 0 ] && \ + type="$netns" + else + [ "$nfs_f" != "nfs" ] && type="rhost" + fi + + if [ "$type" = "lhost" ]; then + calls="$(grep $name /proc/net/rpc/$nfs_f | cut -d' ' -f$field)" + ROD nfsstat $opt | grep -q "$calls" + else + calls=$(tst_rhost_run -c "grep $name /proc/net/rpc/$nfs_f" | \ + cut -d' ' -f$field) + tst_rhost_run -s -c "nfsstat $opt" | grep -q "$calls" + fi + + if ! tst_is_int "$calls"; then + if [ "$type" = "lhost" ]; then + tst_res TINFO "lhost /proc/net/rpc/$nfs_f" + cat /proc/net/rpc/$nfs_f >&2 + else + tst_res TINFO "rhost /proc/net/rpc/$nfs_f" + tst_rhost_run -c "cat /proc/net/rpc/$nfs_f" >&2 + fi + + tst_res TWARN "get_calls: failed to get integer value (args: $@)" fi - calls=$(tst_rhost_run -c "grep $name /proc/net/rpc/$nfs_f" | \ - cut -d' ' -f$field) - tst_rhost_run -s -c "nfsstat -s$opt" | grep -q "$calls" echo "$calls" } @@ -32,18 +65,24 @@ get_calls() # tracking using the 'nfsstat' command and /proc/net/rpc do_test() { + local client_calls server_calls new_server_calls new_client_calls + local client_field server_field root_calls new_root_calls + local client_v=$VERSION server_v=$VERSION + tst_res TINFO "checking RPC calls for server/client" - local server_calls="$(get_calls rpc 2 nfsd)" - local client_calls="$(get_calls rpc 2 nfs)" + server_calls="$(get_calls rpc 2 nfsd)" + client_calls="$(get_calls rpc 2 nfs)" + root_calls="$(get_calls rpc 2 nfs lhost)" tst_res TINFO "calls $server_calls/$client_calls" tst_res TINFO "Checking for tracking of RPC calls for server/client" cat /proc/cpuinfo > nfsstat01.tmp - local new_server_calls="$(get_calls rpc 2 nfsd)" - local new_client_calls="$(get_calls rpc 2 nfs)" + new_server_calls="$(get_calls rpc 2 nfsd)" + new_client_calls="$(get_calls rpc 2 nfs)" + new_root_calls="$(get_calls rpc 2 nfs lhost)" tst_res TINFO "new calls $new_server_calls/$new_client_calls" if [ "$new_server_calls" -le "$server_calls" ]; then @@ -58,24 +97,35 @@ do_test() tst_res TPASS "client RPC calls increased" fi + if [ $NS_STAT_RHOST -ne 0 ]; then + tst_res TINFO "Root NS client RPC calls: $root_calls => $new_root_calls" + + if [ $root_calls -ne $new_root_calls ]; then + tst_res TFAIL "RPC stats leaked between net namespaces" + else + tst_res TPASS "RPC stats stay within net namespaces" + fi + fi + tst_res TINFO "checking NFS calls for server/client" - local field= case $VERSION in - 2) field=13 + 2) client_field=13 server_field=13 ;; - *) field=15 + 3) client_field=15 server_field=15 + ;; + 4*) client_field=24 server_field=31 client_v=4 server_v=4ops ;; esac - server_calls="$(get_calls proc$VERSION $field nfsd)" - client_calls="$(get_calls proc$VERSION $field nfs)" + server_calls="$(get_calls proc$server_v $server_field nfsd)" + client_calls="$(get_calls proc$client_v $client_field nfs)" tst_res TINFO "calls $server_calls/$client_calls" tst_res TINFO "Checking for tracking of NFS calls for server/client" rm -f nfsstat01.tmp - new_server_calls="$(get_calls proc$VERSION $field nfsd)" - new_client_calls="$(get_calls proc$VERSION $field nfs)" + new_server_calls="$(get_calls proc$server_v $server_field nfsd)" + new_client_calls="$(get_calls proc$client_v $client_field nfs)" tst_res TINFO "new calls $new_server_calls/$new_client_calls" if [ "$new_server_calls" -le "$server_calls" ]; then diff --git a/testcases/network/nfs/nfsstat01/nfsstat02.sh b/testcases/network/nfs/nfsstat01/nfsstat02.sh new file mode 100644 index 00000000..75a1a01b --- /dev/null +++ b/testcases/network/nfs/nfsstat01/nfsstat02.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2024 SUSE LLC + +TST_TESTFUNC="do_test" + +# PURPOSE: Check that /proc/net/rpc/nfs exists in nested network namespaces +# d47151b79e32 ("nfs: expose /proc/net/sunrpc/nfs in net namespaces") +# part of patchset +# https://lore.kernel.org/linux-nfs/cover.1708026931.git.josef@toxicpanda.com/ +do_test() +{ + local procfile="/proc/net/rpc/nfs" + + if tst_rhost_run -c "test -e '$procfile'"; then + tst_res TPASS "$procfile exists in net namespaces" + else + tst_res TFAIL "$procfile missing in net namespaces" + fi +} + +# Force use of nested net namespace +unset RHOST + +. nfs_lib.sh +tst_run diff --git a/testcases/network/packet/fanout01.c b/testcases/network/packet/fanout01.c index 4243f840..dc1ff6ce 100755 --- a/testcases/network/packet/fanout01.c +++ b/testcases/network/packet/fanout01.c @@ -85,12 +85,11 @@ void run(void) } static struct tst_test test = { - .min_kver = "3.19", .setup = setup, .test_all = run, .cleanup = cleanup, .needs_root = 1, - .max_runtime = 180, + .min_runtime = 180, .needs_kconfigs = (const char *[]) { "CONFIG_USER_NS=y", "CONFIG_NET_NS=y", diff --git a/testcases/network/rpc/rpc-tirpc/rpc_test.sh b/testcases/network/rpc/rpc-tirpc/rpc_test.sh index cadae552..a038b5bd 100755 --- a/testcases/network/rpc/rpc-tirpc/rpc_test.sh +++ b/testcases/network/rpc/rpc-tirpc/rpc_test.sh @@ -53,6 +53,11 @@ setup() fi fi + if [ "$CLIENT" = 'rpc_pmap_rmtcall' -o "$CLIENT" = 'tirpc_rpcb_rmtcall' ] && \ + rpcbind -v 2>&1 | grep -q 'remote calls: no'; then + tst_brk TCONF "skip due rpcbind compiled without remote calls" + fi + [ -n "$CLIENT" ] || tst_brk TBROK "client program not set" tst_check_cmds $CLIENT $SERVER || tst_brk TCONF "LTP compiled without TI-RPC support?" diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create.c index 98793c64..8578d943 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create.c @@ -52,6 +52,13 @@ int main(int argn, char *argc[]) //First of all, create a client clnt = clnt_create(argc[1], progNum, VERSNUM, proto); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + if (run_mode == 1) { printf("CLIENT : %p\n", clnt); } diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create_stress.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create_stress.c index 58f55d71..667c2a79 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create_stress.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_create/rpc_clnt_create_stress.c @@ -57,8 +57,14 @@ int main(int argn, char *argc[]) for (i = 0; i < nbCall; i++) { clnt = clnt_create(argc[1], progNum, VERSNUM, proto); - if (clnt != NULL) - nbOk++; + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + + nbOk++; } if (run_mode == 1) { diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy.c index f10f15d9..85c0d5fe 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy.c @@ -48,6 +48,13 @@ int main(int argn, char *argc[]) //First of all, create a client clnt = clnt_create(argc[1], progNum, VERSNUM, proto); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call destroy macro clnt_destroy(clnt); diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy_stress.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy_stress.c index 9fef30f6..ba5324c1 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy_stress.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_createdestroy_clnt_destroy/rpc_clnt_destroy_stress.c @@ -56,6 +56,11 @@ int main(int argn, char *argc[]) //First of all, create a client for (i = 0; i < nbCall; i++) { clnt = clnt_create(argc[1], progNum, VERSNUM, proto); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + return 1; + } clnt_destroy(clnt); nbOk++; } diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_pcreateerror/rpc_clnt_pcreateerror.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_pcreateerror/rpc_clnt_pcreateerror.c index 0aaa50c2..03756cf7 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_pcreateerror/rpc_clnt_pcreateerror.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_pcreateerror/rpc_clnt_pcreateerror.c @@ -49,6 +49,13 @@ int main(int argn, char *argc[]) clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + clnt_pcreateerror("#SUCCESS"); //If we are here, test has passed diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perrno/rpc_clnt_perrno.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perrno/rpc_clnt_perrno.c index 489c6c8b..a881a4a0 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perrno/rpc_clnt_perrno.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perrno/rpc_clnt_perrno.c @@ -65,6 +65,13 @@ int main(int argn, char *argc[]) //First of all, create client using top level API clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call remote procedure rslt = clnt_call((CLIENT *) clnt, PROCNUM, (xdrproc_t) xdr_int, (char *)&recVar, // xdr_in (xdrproc_t) xdr_int, (char *)&recVar, // xdr_out diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perror/rpc_clnt_perror.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perror/rpc_clnt_perror.c index 74c6201d..ddf2ec44 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perror/rpc_clnt_perror.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_perror/rpc_clnt_perror.c @@ -65,6 +65,13 @@ int main(int argn, char *argc[]) //First of all, create client using top level API clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call remote procedure rslt = clnt_call((CLIENT *) clnt, PROCNUM, (xdrproc_t) xdr_void, NULL, // xdr_in (xdrproc_t) xdr_int, (char *)&recVar, // xdr_out diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_spcreateerror/rpc_clnt_spcreateerror.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_spcreateerror/rpc_clnt_spcreateerror.c index 65e68dae..a85e8778 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_spcreateerror/rpc_clnt_spcreateerror.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_spcreateerror/rpc_clnt_spcreateerror.c @@ -50,6 +50,13 @@ int main(int argn, char *argc[]) clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + rslt = clnt_spcreateerror("#SUCCESS"); //If we are here, test has passed diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperrno/rpc_clnt_sperrno.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperrno/rpc_clnt_sperrno.c index 1e74c39a..728f35a9 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperrno/rpc_clnt_sperrno.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperrno/rpc_clnt_sperrno.c @@ -66,6 +66,13 @@ int main(int argn, char *argc[]) //First of all, create client using top level API clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call remote procedure rslt = clnt_call((CLIENT *) clnt, PROCNUM, (xdrproc_t) xdr_int, (char *)&recVar, // xdr_in (xdrproc_t) xdr_int, (char *)&recVar, // xdr_out diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperror/rpc_clnt_sperror.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperror/rpc_clnt_sperror.c index fc10f9e0..de473457 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperror/rpc_clnt_sperror.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_clnt_sperror/rpc_clnt_sperror.c @@ -66,6 +66,13 @@ int main(int argn, char *argc[]) //First of all, create client using top level API clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call remote procedure rslt = clnt_call((CLIENT *) clnt, PROCNUM, (xdrproc_t) xdr_void, NULL, // xdr_in (xdrproc_t) xdr_int, (char *)&recVar, // xdr_out diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_auth/rpc_svcerr_auth.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_auth/rpc_svcerr_auth.c index 50232a91..34b1b2bf 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_auth/rpc_svcerr_auth.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_auth/rpc_svcerr_auth.c @@ -62,6 +62,13 @@ int main(int argn, char *argc[]) client = clnt_create(argc[1], progNum, VERSNUM, nettype); /* Call AuthErr RP */ + if (client == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + cs = clnt_call(client, PROCNUM, (xdrproc_t) xdr_int, (char *)&var_snd, (xdrproc_t) xdr_int, (char *)&var_snd, tv); diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_noproc/rpc_svcerr_noproc.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_noproc/rpc_svcerr_noproc.c index 3f9ea914..4483fb6a 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_noproc/rpc_svcerr_noproc.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_noproc/rpc_svcerr_noproc.c @@ -65,6 +65,13 @@ int main(int argn, char *argc[]) client = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (client == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call remote procedure cs = clnt_call((CLIENT *) client, PROCNUM, (xdrproc_t) xdr_int, (char *)&var_snd, // xdr_in (xdrproc_t) xdr_int, (char *)&var_snd, // xdr_out diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_progvers/rpc_svcerr_progvers.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_progvers/rpc_svcerr_progvers.c index 8fa54d9f..9dfc6543 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_progvers/rpc_svcerr_progvers.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_progvers/rpc_svcerr_progvers.c @@ -62,6 +62,13 @@ int main(int argn, char *argc[]) client = clnt_create(argc[1], progNum, VERSNUM, nettype); /* Call wrong version RP */ + if (client == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + cs = clnt_call(client, PROCNUM, (xdrproc_t) xdr_int, (char *)&var_snd, (xdrproc_t) xdr_int, (char *)&var_snd, tv); diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_systemerr/rpc_svcerr_systemerr.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_systemerr/rpc_svcerr_systemerr.c index 2a45f505..17546fe0 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_systemerr/rpc_svcerr_systemerr.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_systemerr/rpc_svcerr_systemerr.c @@ -62,6 +62,13 @@ int main(int argn, char *argc[]) client = clnt_create(argc[1], progNum, VERSNUM, nettype); /* Call SysErr RP */ + if (client == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + cs = clnt_call(client, PROCNUM, (xdrproc_t) xdr_int, (char *)&var_snd, (xdrproc_t) xdr_int, (char *)&var_snd, tv); diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_weakauth/rpc_svcerr_weakauth.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_weakauth/rpc_svcerr_weakauth.c index 563b11a3..a9dd71b5 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_weakauth/rpc_svcerr_weakauth.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_err_svcerr_weakauth/rpc_svcerr_weakauth.c @@ -62,6 +62,13 @@ int main(int argn, char *argc[]) client = clnt_create(argc[1], progNum, VERSNUM, nettype); /* Call WeakAuthErr RP */ + if (client == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + cs = clnt_call(client, PROCNUM, (xdrproc_t) xdr_int, (char *)&var_snd, (xdrproc_t) xdr_int, (char *)&var_snd, tv); diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control.c index 0b37585e..91d08714 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control.c @@ -53,6 +53,13 @@ int main(int argn, char *argc[]) //First of all, create a client clnt = clnt_create(argc[1], progNum, VERSNUM, proto); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + if (run_mode == 1) { printf("CLIENT : %p\n", clnt); } diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control_dataint.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control_dataint.c index 3616d652..10de96e1 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control_dataint.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_control/rpc_clnt_control_dataint.c @@ -54,6 +54,13 @@ int main(int argn, char *argc[]) //First of all, create a client clnt = clnt_create(argc[1], progNum, VERSNUM, proto); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + if (run_mode) { printf("CLIENT : %p\n", clnt); } diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_geterr/rpc_clnt_geterr.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_geterr/rpc_clnt_geterr.c index 9bbce26a..d0505e62 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_geterr/rpc_clnt_geterr.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/rpc/rpc_stdcall_clnt_geterr/rpc_clnt_geterr.c @@ -53,6 +53,13 @@ int main(int argn, char *argc[]) //First of all, create a client clnt = clnt_create(argc[1], progNum, VERSNUM, proto); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + if (run_mode == 1) { printf("CLIENT : %p\n", clnt); } diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_pcreateerror/tirpc_clnt_pcreateerror.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_pcreateerror/tirpc_clnt_pcreateerror.c index 029d158f..81bf37e3 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_pcreateerror/tirpc_clnt_pcreateerror.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_pcreateerror/tirpc_clnt_pcreateerror.c @@ -50,6 +50,13 @@ int main(int argn, char *argc[]) clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + clnt_pcreateerror("#SUCCESS"); //If we are here, test has passed diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perrno/tirpc_clnt_perrno.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perrno/tirpc_clnt_perrno.c index a1b16416..c3ec0888 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perrno/tirpc_clnt_perrno.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perrno/tirpc_clnt_perrno.c @@ -65,6 +65,13 @@ int main(int argn, char *argc[]) //First of all, create client using top level API clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call remote procedure rslt = clnt_call((CLIENT *) clnt, PROCNUM, (xdrproc_t) xdr_void, NULL, // xdr_in (xdrproc_t) xdr_int, (char *)&recVar, // xdr_out diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror.c index 445ecfd3..8a8cdf23 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror.c @@ -65,6 +65,13 @@ int main(int argn, char *argc[]) //First of all, create client using top level API clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Then call remote procedure rslt = clnt_call((CLIENT *) clnt, PROCNUM, (xdrproc_t) xdr_void, NULL, // xdr_in (xdrproc_t) xdr_int, (char *)&recVar, // xdr_out diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror_complex.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror_complex.c index e363e00b..a97bbfa1 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror_complex.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_err_clnt_perror/tirpc_clnt_perror_complex.c @@ -64,6 +64,13 @@ int main(int argn, char *argc[]) total_timeout.tv_usec = 1; /**/ clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //Multiple test case rslt = rpc_call(argc[1], progNum, VERSNUM, PROCNUM, (xdrproc_t) xdr_int, (char *)&sndVar, diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create/tirpc_clnt_create.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create/tirpc_clnt_create.c index eaca1474..6a9aae6c 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create/tirpc_clnt_create.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create/tirpc_clnt_create.c @@ -57,6 +57,13 @@ int main(int argn, char *argc[]) } clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + if (run_mode == 1) { printf("Client after creation : %p\n", clnt); } diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create_timed/tirpc_clnt_create_timed.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create_timed/tirpc_clnt_create_timed.c index 43df4d95..8723ca8c 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create_timed/tirpc_clnt_create_timed.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_create_timed/tirpc_clnt_create_timed.c @@ -62,6 +62,13 @@ int main(int argn, char *argc[]) clnt = clnt_create_timed(argc[1], progNum, VERSNUM, nettype, &tv); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + if (run_mode == 1) { printf("Client after creation : %p\n", clnt); } diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_destroy/tirpc_clnt_destroy.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_destroy/tirpc_clnt_destroy.c index 939ff3e4..bb9a4750 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_destroy/tirpc_clnt_destroy.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_suite/tirpc/tirpc_toplevel_clnt_destroy/tirpc_clnt_destroy.c @@ -58,6 +58,13 @@ int main(int argn, char *argc[]) //first create client clnt = clnt_create(argc[1], progNum, VERSNUM, nettype); + if (clnt == NULL) { + clnt_pcreateerror("err"); + printf("%d\n", rpc_createerr.cf_stat); + + return 1; + } + //then call destroy macro clnt_destroy(clnt); diff --git a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_svc_1/rpc_svc_1.c b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_svc_1/rpc_svc_1.c index d1c4df97..d367691c 100755 --- a/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_svc_1/rpc_svc_1.c +++ b/testcases/network/rpc/rpc-tirpc/tests_pack/rpc_svc_1/rpc_svc_1.c @@ -106,7 +106,7 @@ char *svc_getcaller_test(union u_argument *inVar, SVCXPRT * transp) struct sockaddr_in *sa = NULL; static int result; - sa = svc_getcaller(transp); + sa = (struct sockaddr_in *) svc_getcaller(transp); //If the result is not NULL we consider that function call succeeds //so returns 0 (PASS) result = (sa != NULL) ? 0 : 1; diff --git a/testcases/network/sctp/sctp_big_chunk.c b/testcases/network/sctp/sctp_big_chunk.c index 46748684..6e2d6f2f 100755 --- a/testcases/network/sctp/sctp_big_chunk.c +++ b/testcases/network/sctp/sctp_big_chunk.c @@ -5,8 +5,6 @@ */ /*\ - * [Description] - * * Regression test for the crash caused by over-sized SCTP chunk, * fixed by upstream commit 07f2c7ab6f8d ("sctp: verify size of a new * chunk in _sctp_make_chunk()"). diff --git a/testcases/network/sockets/vsock01.c b/testcases/network/sockets/vsock01.c index 7be71a83..e46f663e 100755 --- a/testcases/network/sockets/vsock01.c +++ b/testcases/network/sockets/vsock01.c @@ -4,8 +4,6 @@ */ /*\ - * [Description] - * * Reproducer of CVE-2021-26708 * * Based on POC https://github.com/jordan9001/vsock_poc. @@ -112,7 +110,8 @@ static struct tst_test test = { .setup = setup, .cleanup = cleanup, .taint_check = TST_TAINT_W | TST_TAINT_D, - .max_runtime = 60, + .runtime = 60, + .min_runtime = 2, .needs_kconfigs = (const char *[]) { "CONFIG_VSOCKETS_LOOPBACK", NULL diff --git a/testcases/network/stress/route/route-lib.sh b/testcases/network/stress/route/route-lib.sh index 163c1542..29aa2e91 100755 --- a/testcases/network/stress/route/route-lib.sh +++ b/testcases/network/stress/route/route-lib.sh @@ -97,10 +97,14 @@ test_netlink() tst_res TINFO "running $cmd $opt" $cmd $opt || ret=$? if [ "$ret" -ne 0 ]; then - [ $((ret & 3)) -ne 0 ] && \ - tst_brk TFAIL "$cmd failed" + if [ $((ret & 3)) -ne 0 ]; then + tst_res TFAIL "$cmd failed" + return + fi + [ $((ret & 32)) -ne 0 ] && \ tst_brk TCONF "not supported configuration" + [ $((ret & 4)) -ne 0 ] && \ tst_res TWARN "$cmd has warnings" fi diff --git a/testcases/network/stress/ssh/ssh-stress.sh b/testcases/network/stress/ssh/ssh-stress.sh index e7c4d45c..c27c27a2 100755 --- a/testcases/network/stress/ssh/ssh-stress.sh +++ b/testcases/network/stress/ssh/ssh-stress.sh @@ -93,8 +93,10 @@ IdentityFile $TST_TMPDIR/id_rsa\n\" > $RHOST_SSH_CONF" test_ssh_connectivity() { - tst_rhost_run -c "$RHOST_SSH 'true >/dev/null 2>&1' >/dev/null" - [ $? -ne 0 ] && tst_brk TFAIL "SSH not reachable" + if ! tst_rhost_run -c "$RHOST_SSH 'true >/dev/null 2>&1' >/dev/null"; then + tst_res TFAIL "SSH not reachable" + return + fi } test1() @@ -121,7 +123,10 @@ test1() [ $? -ne 0 ] && num=$((num + 1)) done - [ $num -ne 0 ] && tst_brk TFAIL "$num ssh processes died unexpectedly during execution" + if [ $num -ne 0 ]; then + tst_res TFAIL "$num ssh processes died unexpectedly during execution" + return + fi test_ssh_connectivity @@ -216,7 +221,10 @@ test3() # Setup an ssh tunnel from the remote host to testhost tst_rhost_run -c "$RHOST_SSH -f -N -L $lport:$rhost:$port /dev/null 2>&1" - [ "$?" -ne 0 ] && tst_brk TFAIL "Failed to create an SSH session with port forwarding" + if [ "$?" -ne 0 ]; then + tst_res TFAIL "Failed to create an SSH session with port forwarding" + return + fi RHOST_PIDS=$(tst_rhost_run -c "pgrep -f '^ssh .*$lport:$rhost:$port'") # Start the TCP traffic clients diff --git a/testcases/network/tcp_cc/dctcp01.sh b/testcases/network/tcp_cc/dctcp01.sh index fbce6f4a..a9e09d13 100755 --- a/testcases/network/tcp_cc/dctcp01.sh +++ b/testcases/network/tcp_cc/dctcp01.sh @@ -7,7 +7,6 @@ TST_SETUP="setup" TST_TESTFUNC="do_test" TST_CLEANUP="cleanup" -TST_MIN_KVER="3.18" cleanup() { diff --git a/testcases/network/tcp_cmds/Makefile b/testcases/network/tcp_cmds/Makefile index 8c1e4f6f..ba8b879e 100755 --- a/testcases/network/tcp_cmds/Makefile +++ b/testcases/network/tcp_cmds/Makefile @@ -7,13 +7,9 @@ top_srcdir ?= ../../.. include $(top_srcdir)/include/mk/env_pre.mk -ifneq ($(WITH_EXPECT),yes) -FILTER_OUT_SUBDIRS := ftp ssh telnet -endif +INSTALL_DEPS := ../datafiles/ascii.sm -INSTALL_DEPS := ../datafiles/bin.sm - -../datafiles/bin.sm: +../datafiles/ascii.sm: $(MAKE) -C .. -f "$(abs_srcdir)/../Makefile" generate include $(top_srcdir)/include/mk/generic_trunk_target.mk diff --git a/testcases/network/tcp_cmds/Makefile.inc b/testcases/network/tcp_cmds/Makefile.inc index 114141b2..0ca5ec0e 100755 --- a/testcases/network/tcp_cmds/Makefile.inc +++ b/testcases/network/tcp_cmds/Makefile.inc @@ -22,7 +22,7 @@ GENERATE_FILE_DIR := ../.. -MAKE_DEPS := $(GENERATE_FILE_DIR)/datafiles/bin.sm +MAKE_DEPS := $(GENERATE_FILE_DIR)/datafiles/ascii.sm $(MAKE_DEPS): $(MAKE) -C $(GENERATE_FILE_DIR) \ diff --git a/testcases/network/tcp_cmds/clockdiff/Makefile b/testcases/network/tcp_cmds/clockdiff/Makefile deleted file mode 100755 index d2e72519..00000000 --- a/testcases/network/tcp_cmds/clockdiff/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2016-2019 Oracle and/or its affiliates. All Rights Reserved. - -top_srcdir ?= ../../../.. - -include $(top_srcdir)/include/mk/env_pre.mk - -INSTALL_TARGETS := clockdiff01.sh - -include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/network/tcp_cmds/clockdiff/clockdiff01.sh b/testcases/network/tcp_cmds/clockdiff/clockdiff01.sh deleted file mode 100755 index 4058fbe1..00000000 --- a/testcases/network/tcp_cmds/clockdiff/clockdiff01.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2016-2019 Oracle and/or its affiliates. All Rights Reserved. -# Author: Alexey Kodanev - -TST_TESTFUNC="do_test" -TST_NEEDS_ROOT=1 -TST_NEEDS_CMDS="cut clockdiff" - - -do_test() -{ - tst_res TINFO "check time delta for $(tst_ipaddr)" - - local output=$(clockdiff $(tst_ipaddr)) - - if [ $? -ne 0 ]; then - tst_res TFAIL "clockdiff failed to get delta" - else - delta=$(echo "$output" | cut -d' ' -f2,3) - if [ "$delta" = "0 0" ]; then - tst_res TPASS "delta is 0 as expected" - else - tst_res TFAIL "not expected data received: '$output'" - fi - fi -} - -. tst_net.sh -tst_run diff --git a/testcases/network/tcp_cmds/ftp/Makefile b/testcases/network/tcp_cmds/ftp/Makefile deleted file mode 100755 index 35022f8b..00000000 --- a/testcases/network/tcp_cmds/ftp/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# -# network/tcp_cmds/ftp testcases Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Ngie Cooper, September 2009 -# - -top_srcdir ?= ../../../.. - -include $(top_srcdir)/include/mk/testcases.mk -include $(abs_srcdir)/../Makefile.inc - -INSTALL_TARGETS := ftp* - -include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/network/tcp_cmds/ftp/ftp01.sh b/testcases/network/tcp_cmds/ftp/ftp01.sh deleted file mode 100755 index 53d1eec5..00000000 --- a/testcases/network/tcp_cmds/ftp/ftp01.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2022 Akihiko Odaki -# Copyright (c) 2003 Manoj Iyer -# Copyright (c) 2001 Robbie Williamson - -TST_TESTFUNC=do_test -TST_CNT=4 -TST_NEEDS_CMDS='awk ftp' -TST_NEEDS_TMPDIR=1 - -RUSER="${RUSER:-root}" -RHOST="${RHOST:-localhost}" - -do_test() -{ - case $1 in - 1) test_get binary;; - 2) test_get ascii;; - 3) test_put binary;; - 4) test_put ascii;; - esac -} - -list_files() -{ - case $1 in - ascii) echo 'ascii.sm ascii.med ascii.lg ascii.jmb';; - binary) echo 'bin.sm bin.med bin.lg bin.jmb';; - esac -} - -test_get() -{ - local file sum1 sum2 - - for file in $(list_files $1); do - { - echo user $RUSER $PASSWD - echo $1 - echo cd $TST_NET_DATAROOT - echo get $file - echo quit - } | ftp -nv $RHOST - - sum1="$(ls -l $file | awk '{print $5}')" - sum2="$(ls -l $TST_NET_DATAROOT/$file | awk '{print $5}')" - rm -f $file - EXPECT_PASS "[ '$sum1' = '$sum2' ]" - done -} - -test_put() -{ - local file sum1 sum2 - - for file in $(list_files $1); do - { - echo user $RUSER $PASSWD - echo lcd $TST_NET_DATAROOT - echo $1 - echo cd $TST_TMPDIR - echo put $file - echo quit - } | ftp -nv $RHOST - - sum1="$(tst_rhost_run -c "sum $TST_TMPDIR/$file" -s | awk '{print $1}')" - sum2="$(sum $TST_NET_DATAROOT/$file | awk '{print $1}')" - tst_rhost_run -c "rm -f $TST_TMPDIR/$file" - EXPECT_PASS "[ '$sum1' = '$sum2' ]" - done -} - -. tst_net.sh -tst_run diff --git a/testcases/network/tcp_cmds/host/Makefile b/testcases/network/tcp_cmds/host/Makefile deleted file mode 100755 index 453951e0..00000000 --- a/testcases/network/tcp_cmds/host/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2009, Cisco Systems Inc. -# Ngie Cooper, July 2009 - -top_srcdir ?= ../../../.. - -include $(top_srcdir)/include/mk/env_pre.mk - -INSTALL_TARGETS := host01.sh - -include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/network/tcp_cmds/host/host01.sh b/testcases/network/tcp_cmds/host/host01.sh deleted file mode 100755 index 6a406749..00000000 --- a/testcases/network/tcp_cmds/host/host01.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2020 Petr Vorel -# Copyright (c) Köry Maincent 2020 -# Copyright (c) Manoj Iyer 2003 -# Copyright (c) Robbie Williamson 2001 -# Copyright (c) International Business Machines Corp., 2000 - -TST_TESTFUNC="do_test" -TST_NEEDS_CMDS="awk grep host hostname tail" - - -do_test() -{ - local lhost="${HOSTNAME:-$(hostname)}" - local addr - - tst_res TINFO "test basic functionality of the host command" - tst_res TINFO "lhost: $lhost" - - if addr=$(host $lhost); then - addr=$(echo "$addr" | grep address | tail -1 | awk '{print $NF}') - if [ -z "$addr" ]; then - tst_brk TFAIL "empty address" - fi - EXPECT_PASS host $addr \>/dev/null - else - tst_brk TFAIL "host $lhost on local machine failed" - fi -} - -. tst_net.sh -tst_run diff --git a/testcases/network/tcp_cmds/ipneigh/ipneigh01.sh b/testcases/network/tcp_cmds/ipneigh/ipneigh01.sh index e67ff5cc..4b818357 100755 --- a/testcases/network/tcp_cmds/ipneigh/ipneigh01.sh +++ b/testcases/network/tcp_cmds/ipneigh/ipneigh01.sh @@ -57,8 +57,10 @@ do_test() for i in $(seq 1 $NUMLOOPS); do - ping$TST_IPV6 -q -c1 $(tst_ipaddr rhost) -I $(tst_iface) > /dev/null || \ - tst_brk TFAIL "cannot ping $(tst_ipaddr rhost)" + if ! ping$TST_IPV6 -q -c1 $(tst_ipaddr rhost) -I $(tst_iface) > /dev/null; then + tst_res TFAIL "cannot ping $(tst_ipaddr rhost)" + return + fi local k local ret=1 @@ -66,19 +68,22 @@ do_test() $SHOW_CMD | grep -q $(tst_ipaddr rhost) if [ $? -eq 0 ]; then ret=0 - break; + break fi tst_sleep 100ms done - [ "$ret" -ne 0 ] && \ - tst_brk TFAIL "$entry_name entry '$(tst_ipaddr rhost)' not listed" + if [ "$ret" -ne 0 ]; then + tst_res TFAIL "$entry_name entry '$(tst_ipaddr rhost)' not listed" + return + fi $DEL_CMD - $SHOW_CMD | grep -q "$(tst_ipaddr rhost).*$(tst_hwaddr rhost)" && \ - tst_brk TFAIL "'$DEL_CMD' failed, entry has " \ - "$(tst_hwaddr rhost)' $i/$NUMLOOPS" + if $SHOW_CMD | grep -q "$(tst_ipaddr rhost).*$(tst_hwaddr rhost)"; then + tst_res TFAIL "'$DEL_CMD' failed, entry has $(tst_hwaddr rhost)' $i/$NUMLOOPS" + return + fi done tst_res TPASS "verified adding/removing $entry_name cache entry" diff --git a/testcases/network/tcp_cmds/telnet/Makefile b/testcases/network/tcp_cmds/telnet/Makefile deleted file mode 100755 index 90e879de..00000000 --- a/testcases/network/tcp_cmds/telnet/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# -# network/tcp_cmds/telnet testcases Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Ngie Cooper, July 2009 -# - -top_srcdir ?= ../../../.. - -include $(top_srcdir)/include/mk/env_pre.mk - -INSTALL_TARGETS := telnet01.sh - -include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/network/tcp_cmds/telnet/telnet01.sh b/testcases/network/tcp_cmds/telnet/telnet01.sh deleted file mode 100755 index 93343b99..00000000 --- a/testcases/network/tcp_cmds/telnet/telnet01.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/sh -# Copyright (c) International Business Machines Corp., 2000 -# -# 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 -# -# 03/01 Robbie Williamson (robbiew@us.ibm.com) - -TCID="telnet01" -TST_TOTAL=1 - -TST_USE_LEGACY_API=1 - -setup() -{ - tst_require_cmds telnet expect - - if [ -z $RUSER ]; then - RUSER=root - fi - - if [ -z $PASSWD ]; then - tst_brkm TCONF "Please set PASSWD for $RUSER." - fi - - if [ -z $RHOST ]; then - tst_brkm TCONF "Please set RHOST." - fi - - if [ -z $LOOPCOUNT ]; then - LOOPCOUNT=25 - fi -} - -do_test() -{ - tst_resm TINFO "Starting" - - for i in $(seq 1 ${LOOPCOUNT}) - do - telnet_test || return 1 - done -} - -telnet_test() -{ - tst_resm TINFO "login with telnet($i/$LOOPCOUNT)" - - expect -c " - spawn telnet $RHOST - - expect -re \"login:\" - send \"$RUSER\r\" - - expect -re \"Password:\" - send \"$PASSWD\r\" - - expect { - \"incorrect\" { - exit 1 - } \"$RUSER@\" { - send \"LC_ALL=C ls -l /etc/hosts | \\ - wc -w > $RUSER.$RHOST\rexit\r\"; - exp_continue} - } - - " > /dev/null || return 1 - - tst_resm TINFO "checking telnet status($i/$LOOPCOUNT)" - tst_rhost_run -u $RUSER -c "grep -q 9 $RUSER.$RHOST" || return 1 - tst_rhost_run -u $RUSER -c "rm -f $RUSER.$RHOST" -} - -. tst_net.sh - -setup - -do_test -if [ $? -ne 0 ]; then - tst_resm TFAIL "Test $TCID failed." -else - tst_resm TPASS "Test $TCID succeeded." -fi - -tst_exit diff --git a/testcases/network/tcp_fastopen/tcp_fastopen_run.sh b/testcases/network/tcp_fastopen/tcp_fastopen_run.sh index 88438c3e..a3dad55a 100755 --- a/testcases/network/tcp_fastopen/tcp_fastopen_run.sh +++ b/testcases/network/tcp_fastopen/tcp_fastopen_run.sh @@ -7,7 +7,6 @@ TST_SETUP="setup" TST_TESTFUNC="test" TST_CNT=2 TST_CLEANUP="cleanup" -TST_MIN_KVER="3.7" TST_NEEDS_TMPDIR=1 TST_NEEDS_ROOT=1 TST_NEEDS_CMDS="tc" @@ -38,10 +37,6 @@ cleanup() setup() { - if tst_kvcmp -lt "3.16" && [ "$TST_IPV6" ]; then - tst_brk TCONF "test must be run with kernel 3.16 or newer" - fi - ROD tc qdisc add dev $(tst_iface) root netem delay 100 } diff --git a/testcases/network/virt/virt_lib.sh b/testcases/network/virt/virt_lib.sh index e919bc3a..2ee57b79 100755 --- a/testcases/network/virt/virt_lib.sh +++ b/testcases/network/virt/virt_lib.sh @@ -175,7 +175,7 @@ virt_multiple_add_test() for i in $(seq $start_id $max); do virt_add ltp_v$i id $i $opt || \ - tst_brk TFAIL "failed to create 'ltp_v0 $opt'" + tst_brk TBROK "failed to create 'ltp_v$i $opt'" ROD_SILENT "ip link set ltp_v$i up" done @@ -197,7 +197,7 @@ virt_add_delete_test() for i in $(seq 0 $max); do virt_add ltp_v0 $opt || \ - tst_brk TFAIL "failed to create 'ltp_v0 $opt'" + tst_brk TBROK "failed to create 'ltp_v0 $opt'" ROD_SILENT "ip link set ltp_v0 up" ROD_SILENT "ip link delete ltp_v0" done @@ -284,10 +284,10 @@ virt_compare_netperf() local expect_res="${1:-pass}" local opts="$2" - tst_netload -H $ip_virt_remote $opts -d res_ipv4 -e $expect_res \ + tst_netload -H $ip_virt_remote $opts -c res_ipv4 -e $expect_res \ -D ltp_v0 || ret1="fail" - tst_netload -H ${ip6_virt_remote} $opts -d res_ipv6 -e $expect_res \ + tst_netload -H ${ip6_virt_remote} $opts -c res_ipv6 -e $expect_res \ -D ltp_v0 || ret2="fail" [ "$ret1" = "fail" -o "$ret2" = "fail" ] && return @@ -295,7 +295,7 @@ virt_compare_netperf() local vt="$(cat res_ipv4)" local vt6="$(cat res_ipv6)" - tst_netload -H $(tst_ipaddr rhost) $opts -d res_lan + tst_netload -H $(tst_ipaddr rhost) $opts -c res_lan local lt="$(cat res_lan)" tst_res TINFO "time lan IPv${TST_IPVER}($lt) $virt_type IPv4($vt) and IPv6($vt6) ms" diff --git a/testcases/network/xinetd/xinetd_tests.sh b/testcases/network/xinetd/xinetd_tests.sh deleted file mode 100755 index 505dae5d..00000000 --- a/testcases/network/xinetd/xinetd_tests.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2020 Petr Vorel -# Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved. -# Copyright (c) International Business Machines Corp., 2001 - -TST_SETUP="setup" -TST_CLEANUP="cleanup" -TST_NEEDS_CMDS="diff telnet in.telnetd xinetd" -TST_NEEDS_TMPDIR=1 -TST_TESTFUNC="do_test" -TST_CNT=2 - -. daemonlib.sh - -setup() -{ - [ -f "/usr/lib/systemd/system/telnet.socket" ] && \ - tst_brk TCONF "xinetd doesn't manage telnet" - - check_addr="127.0.0.1" - ip a | grep -q inet6 && check_addr="$check_addr ::1" - - cat > tst_xinetd.conf.1 <<-EOF -defaults -{ - instances = 25 - log_type = FILE /var/log/servicelog - log_on_success = HOST PID - log_on_failure = HOST - disabled = telnet -} -EOF - - cat > tst_xinetd.conf.2 <<-EOF -defaults -{ - instances = 25 - log_type = FILE /var/log/servicelog - log_on_success = HOST PID - log_on_failure = HOST - # disabled = telnet -} - -service telnet -{ - socket_type = stream - protocol = tcp - wait = no - user = root - server = /usr/sbin/in.telnetd - server_args = -n - no_access = - flags = IPv6 -} -EOF - ROD mv /etc/xinetd.conf xinetd.conf.orig -} - -cleanup() -{ - [ -f xinetd.conf.orig ] && \ - mv xinetd.conf.orig /etc/xinetd.conf - - restart_daemon xinetd -} - -restart_xinetd() -{ - tst_res TINFO "restart xinetd" - restart_daemon xinetd > tst_xinetd.out 2>&1 - if [ $? -ne 0 ]; then - cat tst_xinetd.out - tst_brk TBROK "unable to restart service with telnet disabled" - fi - - grep -qi "fail" tst_xinetd.out && \ - tst_brk TBROK "xinetd failed to restart" -} - -xinetd_test() -{ - local cnt=$1 - local desc="$2" - local pattern="$3" - local a p - - tst_res TINFO "install the new config file with telnet $desc" - ROD mv tst_xinetd.conf.$cnt /etc/xinetd.conf - restart_xinetd - - for a in $check_addr; do - p=$(echo $pattern | sed "s/ADDR/$a/") - echo '' | telnet $a 2>&1 | grep -qiE "$p" - [ $? -ne 0 ] && \ - tst_brk TFAIL "not expected output for 'telnet $a'" - done - tst_res TPASS "expected output with telnet $desc" -} - -do_test() -{ - case $1 in - 1) xinetd_test $1 "disabled" \ - "telnet: (connect to address ADDR|Unable to connect to remote host): Connection refused";; - 2) xinetd_test $1 "enabled" \ - "Connection closed by foreign host";; - esac -} - -. tst_net.sh -tst_run diff --git a/testcases/open_posix_testsuite/Makefile b/testcases/open_posix_testsuite/Makefile index 8b4c8c0a..c0ccd499 100755 --- a/testcases/open_posix_testsuite/Makefile +++ b/testcases/open_posix_testsuite/Makefile @@ -31,11 +31,16 @@ AUTOGENERATED_FILES = include/mk/config.mk .PHONY: ac-clean ac-clean: clean + $(RM) -rf autom4te.cache + $(RM) -f config.log config.status + +.PHONE: ac-maintainer-clean +ac-maintainer-clean: + $(RM) -f configure .PHONY: clean clean: $(RM) -f $(LOGFILE)* - $(RM) -f config.log config.status @for dir in $(SUBDIRS) tools; do \ $(MAKE) -C $$dir clean >/dev/null; \ done @@ -51,6 +56,9 @@ distclean-makefiles: $(MAKE) -C $$dir $@; \ done +.PHONY: maintainer-clean +maintainer-clean: distclean-makefiles ac-maintainer-clean + $(AUTOGENERATED_FILES): $(top_builddir)/config.status $(SHELL) $^ diff --git a/testcases/open_posix_testsuite/conformance/definitions/aio_h/4-1.c b/testcases/open_posix_testsuite/conformance/definitions/aio_h/4-1.c index f30f14df..9283e1de 100755 --- a/testcases/open_posix_testsuite/conformance/definitions/aio_h/4-1.c +++ b/testcases/open_posix_testsuite/conformance/definitions/aio_h/4-1.c @@ -16,7 +16,7 @@ static ssize_t (*dummy4) (struct aiocb*) = aio_return; static int (*dummy5) (const struct aiocb* const[], int, const struct timespec *) = aio_suspend; static int (*dummy6) (struct aiocb *) = aio_write; -static int (*dummy7) (int, struct aiocb *restrict const [restrict], +static int (*dummy7) (int, struct aiocb *const [restrict], int, struct sigevent *restrict) = lio_listio; int main(void) diff --git a/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c b/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c index 34b26324..75cd838a 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/aio_cancel/7-1.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004, Bull SA. All rights reserved. + * Copyright (c) 2025 SUSE LLC * Created by: Laurent.Vivier@bull.net * This file is licensed under the GPL license. For the full content * of this license, see the COPYING file at the top level of this @@ -14,145 +15,176 @@ * * method: * - * queue a lot of aio_write() to a given file descriptor - * then cancel all operation belonging to this file descriptor + * queue multiple aio_write()s to a given socket + * wait for a specific task to start and block on full socket buffer + * then cancel all operations belonging to this socket * check result of each operation: - * - if aio_error() is EINPROGRESS and aio_cancel() is not AIO_NOTCANCELED - * result is failed - * - if aio_error() is succes (0) and aio_cancel() is AIO_NOTCANCELED - * result is susccess - * - otherwise result is unresolved - * + * - aio_cancel() must return AIO_NOTCANCELED + * - aio_error() must return 0 for writes before the blocked task + * - aio_error() must return EINPROGRESS for the blocked task + * - aio_error() must return ECANCELED for writes after the blocked task + * If any of the above conditions is not met, fail the test. + * Otherwise pass. */ #include -#include #include -#include -#include #include #include #include +#include #include +#include #include "posixtest.h" -#include "tempfile.h" #define TNAME "aio_cancel/7-1.c" -#define BUF_NB 128 -#define BUF_SIZE 1024 +#define BUF_NB 8 +#define BLOCKED_TASK 2 + +static int fds[2]; +static struct aiocb aiocb[BUF_NB]; + +static void cleanup(void) +{ + int i, ret; + + for (i = 0; i < BUF_NB; i++) { + if (!aiocb[i].aio_buf) + break; + + ret = aio_error(&aiocb[i]); + + /* flush written data from the socket */ + if (ret == 0 || ret == EINPROGRESS) { + read(fds[1], (void *)aiocb[i].aio_buf, + aiocb[i].aio_nbytes); + } + + free((void *)aiocb[i].aio_buf); + } + + close(fds[0]); + close(fds[1]); +} int main(void) { - char tmpfname[PATH_MAX]; - int fd; - struct aiocb *aiocb[BUF_NB]; int i; - int in_progress; int gret; + int bufsize; + int exp_ret; + socklen_t argsize = sizeof(bufsize); + const struct timespec sleep_ts = { .tv_sec = 0, .tv_nsec = 10000000 }; if (sysconf(_SC_ASYNCHRONOUS_IO) < 200112L) return PTS_UNSUPPORTED; - PTS_GET_TMP_FILENAME(tmpfname, "pts_aio_cancel_7_1"); - unlink(tmpfname); - fd = open(tmpfname, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR); - if (fd == -1) { - printf(TNAME " Error at open(): %s\n", strerror(errno)); + gret = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds); + if (gret == -1) { + printf(TNAME " Error creating sockets(): %s\n", + strerror(errno)); return PTS_UNRESOLVED; } - unlink(tmpfname); + gret = getsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &bufsize, &argsize); + if (gret == -1) { + printf(TNAME " Error reading socket buffer size: %s\n", + strerror(errno)); + cleanup(); + return PTS_UNRESOLVED; + } + + /* Socket buffer size is twice the maximum message size */ + bufsize /= 2; /* create AIO req */ for (i = 0; i < BUF_NB; i++) { - aiocb[i] = calloc(1, sizeof(struct aiocb)); + aiocb[i].aio_fildes = fds[0]; + aiocb[i].aio_buf = malloc(bufsize); - if (aiocb[i] == NULL) { + if (aiocb[i].aio_buf == NULL) { printf(TNAME " Error at malloc(): %s\n", strerror(errno)); - close(fd); + cleanup(); return PTS_UNRESOLVED; } - aiocb[i]->aio_fildes = fd; - aiocb[i]->aio_buf = malloc(BUF_SIZE); + aiocb[i].aio_nbytes = bufsize; + aiocb[i].aio_offset = 0; + aiocb[i].aio_sigevent.sigev_notify = SIGEV_NONE; - if (aiocb[i]->aio_buf == NULL) { - printf(TNAME " Error at malloc(): %s\n", - strerror(errno)); - close(fd); - return PTS_UNRESOLVED; - } - - aiocb[i]->aio_nbytes = BUF_SIZE; - aiocb[i]->aio_offset = 0; - aiocb[i]->aio_sigevent.sigev_notify = SIGEV_NONE; - - if (aio_write(aiocb[i]) == -1) { + if (aio_write(&aiocb[i]) == -1) { printf(TNAME " loop %d: Error at aio_write(): %s\n", i, strerror(errno)); - close(fd); + cleanup(); return PTS_FAIL; } } - /* try to cancel all - * we hope to have enough time to cancel at least one - */ - gret = aio_cancel(fd, NULL); - if (gret == -1) { - printf(TNAME " Error at aio_cancel(): %s\n", strerror(errno)); - close(fd); + /* wait for write #2 to start and get blocked by full socket buffer */ + for (i = 0; i < 1000; i++) { + gret = aio_error(&aiocb[BLOCKED_TASK - 1]); + nanosleep(&sleep_ts, NULL); + + if (gret <= 0) + break; + } + + if (gret) { + printf(TNAME " Task #%d failed to complete: %s\n", + BLOCKED_TASK - 1, strerror(gret == -1 ? errno : gret)); + cleanup(); return PTS_FAIL; } - do { - in_progress = 0; - for (i = 0; i < BUF_NB; i++) { - int ret = aio_error(aiocb[i]); + /* try to cancel all */ + gret = aio_cancel(fds[0], NULL); + if (gret == -1) { + printf(TNAME " Error at aio_cancel(): %s\n", strerror(errno)); + cleanup(); + return PTS_FAIL; + } - switch (ret) { - case -1: - printf(TNAME " Error at aio_error(): %s\n", - strerror(errno)); - close(fd); - return PTS_FAIL; - break; - case EINPROGRESS: - /* at this point, all operations should be: - * canceled - * or in progress - * with aio_cancel() == AIO_NOTCANCELED - */ - if (gret != AIO_NOTCANCELED) { - printf(TNAME - " Error at aio_error(): %s\n", - strerror(errno)); - close(fd); - return PTS_FAIL; - } + if (gret != AIO_NOTCANCELED) { + printf(TNAME " Unexpected aio_cancel() return value: %s\n", + strerror(gret)); + cleanup(); + return PTS_FAIL; + } - in_progress = 1; - break; - case 0: - /* we seek one not canceled and check why. - * (perhaps) it has not been canceled - * because it was in progress - * during the cancel operation - */ - if (gret == AIO_NOTCANCELED) { - printf("Test PASSED\n"); - close(fd); - return PTS_PASS; - } - break; + /* + * check write results, expected values: + * - 0 for the first two writes + * - EINPROGRESS for the third + * - ECANCELED for the rest + */ + for (i = 0, exp_ret = 0; i < BUF_NB; i++) { + int ret = aio_error(&aiocb[i]); + + if (i == BLOCKED_TASK) { + if (ret == EINPROGRESS) { + exp_ret = ECANCELED; + continue; } + + printf(TNAME " Bad task #%d result: %s " + "(expected EINPROGRESS)\n", + i, strerror(ret)); + cleanup(); + return PTS_FAIL; } - } while (in_progress); - close(fd); + if (ret != exp_ret) { + printf(TNAME " Bad task #%d result: %s (expected %s)\n", + i, strerror(ret), strerror(exp_ret)); + cleanup(); + return PTS_FAIL; + } + } - return PTS_UNRESOLVED; + cleanup(); + printf("Test PASSED\n"); + return PTS_PASS; } diff --git a/testcases/open_posix_testsuite/conformance/interfaces/mmap/24-1.c b/testcases/open_posix_testsuite/conformance/interfaces/mmap/24-1.c index 6cc8349e..2ab7b646 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/mmap/24-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/mmap/24-1.c @@ -9,13 +9,20 @@ * The mmap() function shall fail if: * [ENOMEM] MAP_FIXED was specified, and the range [addr,addr+len) * exceeds that allowed for the address space of a process; - * or, if MAP_FIXED was not specified and - * there is insufficient room in the address space to effect the mapping. + * or, if MAP_FIXED was not specified and there is insufficient room + * in the address space to effect the mapping; + * or, if the process exceeds the maximum number of allowed memory mappings + * (as defined by /proc/sys/vm/max_map_count). * * Test Steps: - * 1. In a very long loop, keep mapping a shared memory object, - * until there this insufficient room in the address space; - * 3. Should get ENOMEM. + * 1. In a very long loop, continuously map a shared memory object without + * unmapping previous ones. + * 2. The loop continues until mmap() fails with ENOMEM. + * + * Note: + * This failure may occur due to either exhausting the process's + * virtual address space, or hitting the system-wide limit on + * the number of memory mappings (especially on systems with large RAM). */ #include @@ -31,12 +38,42 @@ #include #include "posixtest.h" +#define MAX_MAP_COUNT_PATH "/proc/sys/vm/max_map_count" +#define MAP_COUNT_LIMIT 65530 + +void proc_write_val(const char *path, size_t val) +{ + FILE *procfile; + + procfile = fopen(path, "r+"); + + if (!procfile) { + printf("Warning: Could not open %s\n", path); + return; + } + + fprintf(procfile, "%zu", val); + fclose(procfile); +} + int main(void) { char tmpfname[256]; void *pa; - size_t len; + size_t len, max_map_count = 0; int fd; + FILE *procfile; + + /* Change vm.max_map_count to avoid OOM */ + procfile = fopen(MAX_MAP_COUNT_PATH, "r"); + + if (procfile) { + fscanf(procfile, "%zu", &max_map_count); + fclose(procfile); + } + + if (max_map_count > MAP_COUNT_LIMIT) + proc_write_val(MAX_MAP_COUNT_PATH, MAP_COUNT_LIMIT); /* Size of the shared memory object */ size_t shm_size = 1024; @@ -78,5 +115,10 @@ int main(void) close(fd); printf("Test FAILED: Did not get ENOMEM as expected\n"); + + /* Restore original vm.max_map_count */ + if (max_map_count > MAP_COUNT_LIMIT) + proc_write_val(MAX_MAP_COUNT_PATH, max_map_count); + return PTS_FAIL; } diff --git a/testcases/open_posix_testsuite/conformance/interfaces/pthread_cond_timedwait/4-3.c b/testcases/open_posix_testsuite/conformance/interfaces/pthread_cond_timedwait/4-3.c index 5a49ffc3..305ab045 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/pthread_cond_timedwait/4-3.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/pthread_cond_timedwait/4-3.c @@ -69,7 +69,10 @@ typedef struct { static struct { pthread_mutex_t mtx; pthread_cond_t cnd; -} data; +} data = { + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_COND_INITIALIZER +}; /* the following function keeps on sending the signal to the process */ static void *sendsig(void *arg) diff --git a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-1.c b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-1.c index 288eb3e3..3b27a0bb 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-1.c @@ -6,12 +6,13 @@ * Test that pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) * - * If the Thread Execution Scheduling option is supported, - * and the threads involved in the lock are executing with the - * scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not - * acquire the lock if a writer holds the lock or if writers of - * higher or equal priority are blocked on the lock; - * otherwise, the calling thread shall acquire the lock. + * If the Thread Execution Scheduling option is supported, + * and the threads that hold or are blocked on the lock are + * executing with the scheduling policies SCHED_FIFO or SCHED_RR, + * the calling thread shall not acquire the lock if a writer + * holds the lock or if the calling thread does not already hold + * a read lock and writers of higher or equal priority are blocked + * on the lock; otherwise, the calling thread shall acquire the lock. * * In this case, we test "higher priority writer block" * @@ -143,13 +144,19 @@ int main(void) int cnt = 0; pthread_t rd_thread, wr_thread; int priority; + pthread_rwlockattr_t attr; + + pthread_rwlockattr_init(&attr); +#ifdef __GNUC__ + pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); +#endif /* main thread needs to have the highest priority */ priority = sched_get_priority_min(TRD_POLICY) + 2; set_priority(pthread_self(), TRD_POLICY, priority); printf("main: has priority: %d\n", priority); - if (pthread_rwlock_init(&rwlock, NULL) != 0) { + if (pthread_rwlock_init(&rwlock, &attr) != 0) { printf("main: Error at pthread_rwlock_init()\n"); return PTS_UNRESOLVED; } diff --git a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c index 3593e4c7..4799b1e8 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c @@ -6,12 +6,13 @@ * Test that pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) * - * If the Thread Execution Scheduling option is supported, - * and the threads involved in the lock are executing with the - * scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not - * acquire the lock if a writer holds the lock or if writers of - * higher or equal priority are blocked on the lock; - * otherwise, the calling thread shall acquire the lock. + * If the Thread Execution Scheduling option is supported, + * and the threads that hold or are blocked on the lock are + * executing with the scheduling policies SCHED_FIFO or SCHED_RR, + * the calling thread shall not acquire the lock if a writer + * holds the lock or if the calling thread does not already hold + * a read lock and writers of higher or equal priority are blocked + * on the lock; otherwise, the calling thread shall acquire the lock. * * In this case, we test "equal priority writer block" * @@ -143,12 +144,18 @@ int main(void) int cnt = 0; pthread_t rd_thread, wr_thread; int priority; + pthread_rwlockattr_t attr; + + pthread_rwlockattr_init(&attr); +#ifdef __GNUC__ + pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); +#endif /* main thread needs to have the highest priority */ priority = sched_get_priority_min(TRD_POLICY) + 2; set_priority(pthread_self(), TRD_POLICY, priority); - if (pthread_rwlock_init(&rwlock, NULL) != 0) { + if (pthread_rwlock_init(&rwlock, &attr) != 0) { printf("main: Error at pthread_rwlock_init()\n"); return PTS_UNRESOLVED; } diff --git a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-3.c b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-3.c index 40170dbb..aa1511ba 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-3.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-3.c @@ -6,12 +6,13 @@ * Test that pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) * - * If the Thread Execution Scheduling option is supported, - * and the threads involved in the lock are executing with the - * scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not - * acquire the lock if a writer holds the lock or if writers of - * higher or equal priority are blocked on the lock; - * otherwise, the calling thread shall acquire the lock. + * If the Thread Execution Scheduling option is supported, + * and the threads that hold or are blocked on the lock are + * executing with the scheduling policies SCHED_FIFO or SCHED_RR, + * the calling thread shall not acquire the lock if a writer + * holds the lock or if the calling thread does not already hold + * a read lock and writers of higher or equal priority are blocked + * on the lock; otherwise, the calling thread shall acquire the lock. * * In this case, reader has higher priority than the writer * diff --git a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/assertions.xml b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/assertions.xml index 07263b3d..deb318cb 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/assertions.xml +++ b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/assertions.xml @@ -6,13 +6,14 @@ no writers blocked on the lock. - - If the Thread Execution Scheduling option is supported, - and the threads involved in the lock are executing with the - scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not - acquire the lock if a writer holds the lock or if writers of - higher or equal priority are blocked on the lock; - otherwise, the calling thread shall acquire the lock. + + If the Thread Execution Scheduling option is supported, + and the threads that hold or are blocked on the lock are + executing with the scheduling policies SCHED_FIFO or SCHED_RR, + the calling thread shall not acquire the lock if a writer + holds the lock or if the calling thread does not already hold + a read lock and writers of higher or equal priority are blocked + on the lock; otherwise, the calling thread shall acquire the lock. diff --git a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_unlock/4-1.c b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_unlock/4-1.c index 2db9e4cc..d4a579ed 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_unlock/4-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_unlock/4-1.c @@ -31,8 +31,8 @@ int main(void) static pthread_rwlock_t rwlock; int rc; -#ifdef __linux__ - printf("Unlocking uninitialized rwlock is undefined on Linux\n"); +#if defined(__linux__) || defined(__NuttX__) + printf("Unlocking uninitialized rwlock is undefined on this OS\n"); return PTS_UNSUPPORTED; #endif diff --git a/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/2-1.c b/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/2-1.c index 655e3510..61603cee 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/2-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/2-1.c @@ -20,20 +20,45 @@ #include #include #include +#include #include "posixtest.h" +#include "proc.h" #define TEST "2-1" +#define SHM_NAME "/posixtest_2-1" #define FUNCTION "sem_timedwait" #define ERROR_PREFIX "unexpected error: " FUNCTION " " TEST ": " int main(void) { - sem_t mysemp; + sem_t *mysemp; struct timespec ts; - int pid; + int fd, pid; + + fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + perror(ERROR_PREFIX "shm_open()"); + return PTS_UNRESOLVED; + } + + if (ftruncate(fd, sizeof(*mysemp)) == -1) { + perror(ERROR_PREFIX "ftruncate()"); + return PTS_UNRESOLVED; + } + + if (shm_unlink(SHM_NAME) != 0) { + perror(ERROR_PREFIX "shm_unlink()"); + return PTS_UNRESOLVED; + } + + mysemp = mmap(NULL, sizeof(*mysemp), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mysemp == MAP_FAILED) { + perror(ERROR_PREFIX "mmap"); + return PTS_UNRESOLVED; + } /* Semaphore started out locked */ - if (sem_init(&mysemp, 0, 0) == -1) { + if (sem_init(mysemp, 1, 0) == -1) { perror(ERROR_PREFIX "sem_init"); return PTS_UNRESOLVED; } @@ -44,19 +69,20 @@ int main(void) ts.tv_sec = time(NULL) + 2; ts.tv_nsec = 0; - if (sem_timedwait(&mysemp, &ts) == -1) { + if (sem_timedwait(mysemp, &ts) == -1) { puts("TEST FAILED"); return PTS_FAIL; } else { puts("TEST PASSED"); - sem_destroy(&mysemp); + sem_destroy(mysemp); return PTS_PASS; } } else if (pid > 0) // parent to unlock semaphore { int i; - sleep(1); - if (sem_post(&mysemp) == -1) { + + tst_process_state_wait3(pid, 'S', 1); + if (sem_post(mysemp) == -1) { perror(ERROR_PREFIX "sem_post"); return PTS_FAIL; } @@ -65,12 +91,18 @@ int main(void) return PTS_UNRESOLVED; } - if (!WEXITSTATUS(i)) { + if (!WIFEXITED(i) || WEXITSTATUS(i)) { return PTS_FAIL; } puts("TEST PASSED"); - sem_destroy(&mysemp); + sem_destroy(mysemp); + if (munmap(mysemp, sizeof(*mysemp)) == -1) { + perror(ERROR_PREFIX "munmap"); + return PTS_UNRESOLVED; + } return PTS_PASS; } + + close(fd); return PTS_UNRESOLVED; } diff --git a/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/3-1.c b/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/3-1.c index fb6f2e6c..739fe86a 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/3-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/3-1.c @@ -15,7 +15,7 @@ #include #include #include -#if __linux__ +#ifdef __linux__ #include #endif #include diff --git a/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/9-1.c b/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/9-1.c index f9175839..ee7ad7ae 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/9-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/sem_timedwait/9-1.c @@ -21,6 +21,7 @@ #include #include #include "posixtest.h" +#include "proc.h" #define TEST "9-1" #define FUNCTION "sem_timedwait" @@ -79,7 +80,8 @@ int main(void) } else { // parent to send a signal to child int i; - sleep(1); + + tst_process_state_wait3(pid, 'S', 1); (void)kill(pid, SIGABRT); // send signal to child if (wait(&i) == -1) { perror("Error waiting for child to exit\n"); diff --git a/testcases/open_posix_testsuite/conformance/interfaces/shm_open/37-1.c b/testcases/open_posix_testsuite/conformance/interfaces/shm_open/37-1.c index 09b4d665..d9da1cab 100755 --- a/testcases/open_posix_testsuite/conformance/interfaces/shm_open/37-1.c +++ b/testcases/open_posix_testsuite/conformance/interfaces/shm_open/37-1.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Test that the shm_open() function sets errno = EINVAL if the shm_open() * operation is not supported for the given name. diff --git a/testcases/open_posix_testsuite/functional/mqueues/send_rev_1.c b/testcases/open_posix_testsuite/functional/mqueues/send_rev_1.c index 46eef837..7c7d2f76 100755 --- a/testcases/open_posix_testsuite/functional/mqueues/send_rev_1.c +++ b/testcases/open_posix_testsuite/functional/mqueues/send_rev_1.c @@ -57,23 +57,6 @@ int main(void) ret_code = PTS_UNRESOLVED; break; case 0: - mq_getattr(mq, &attr); - for (i = 0; i < MAX_MSG && ret_code == PTS_PASS; i++) { - printf("[%d] s_msg_ptr is '%s' \n", i + 1, - s_msg_ptr[i]); - printf("Prepare to send message...\n"); - if (-1 == mq_send(mq, s_msg_ptr[i], attr.mq_msgsize, 1)) { - perror("mq_send doesn't return success \n"); - ret_code = PTS_UNRESOLVED; - } else { - printf("Process %ld send message '%s' to " - "process %ld \n", - (long)getpid(), s_msg_ptr[i], (long)pid); - } - } - (void)wait(NULL); - break; - default: printf("Enter into child process...\n"); mq_getattr(mq, &attr); for (i = 0; i < MAX_MSG && ret_code == PTS_PASS; i++) { @@ -91,7 +74,23 @@ int main(void) } } exit(ret_code); - + break; + default: + mq_getattr(mq, &attr); + for (i = 0; i < MAX_MSG && ret_code == PTS_PASS; i++) { + printf("[%d] s_msg_ptr is '%s' \n", i + 1, + s_msg_ptr[i]); + printf("Prepare to send message...\n"); + if (-1 == mq_send(mq, s_msg_ptr[i], strlen(s_msg_ptr[i]) + 1, 1)) { + perror("mq_send doesn't return success \n"); + ret_code = PTS_UNRESOLVED; + } else { + printf("Process %ld send message '%s' to " + "process %ld \n", + (long)getpid(), s_msg_ptr[i], (long)pid); + } + } + (void)wait(NULL); break; } diff --git a/testcases/open_posix_testsuite/functional/mqueues/send_rev_2.c b/testcases/open_posix_testsuite/functional/mqueues/send_rev_2.c index a0a49d15..22ecbb22 100755 --- a/testcases/open_posix_testsuite/functional/mqueues/send_rev_2.c +++ b/testcases/open_posix_testsuite/functional/mqueues/send_rev_2.c @@ -43,7 +43,7 @@ static int *send_1(void *mq) printf("Enter into send_1 \n"); for (i = 0; i < MAX_MSG; i++) { - if (-1 == mq_send(mq1, s_msg_ptr[i], MSG_SIZE, i)) { + if (-1 == mq_send(mq1, s_msg_ptr[i], strlen(s_msg_ptr[i]) + 1, i)) { perror("mq_send doesn't return success \n"); pthread_exit((void *)1); } @@ -61,7 +61,7 @@ static int *send_2(void *mq) printf("Enter into send_2 \n"); for (i = 0; i < MAX_MSG; i++) { - if (-1 == mq_send(mq2, s_msg_ptr[i], MSG_SIZE, i)) { + if (-1 == mq_send(mq2, s_msg_ptr[i], strlen(s_msg_ptr[i]) + 1, i)) { perror("mq_send doesn't return success \n"); pthread_exit((void *)1); } diff --git a/testcases/open_posix_testsuite/include/mk/config.mk.in b/testcases/open_posix_testsuite/include/mk/config.mk.in index c9a4b5c5..20422e0f 100644 --- a/testcases/open_posix_testsuite/include/mk/config.mk.in +++ b/testcases/open_posix_testsuite/include/mk/config.mk.in @@ -14,4 +14,4 @@ testdir_rel := @testdir@ testdir := ${prefix}/${testdir_rel} testdir_bin := ${testdir}/bin -CFLAGS += -std=c99 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -W -Wall +CFLAGS += -std=c99 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -Wall -Wextra diff --git a/testcases/open_posix_testsuite/include/posixtest.h b/testcases/open_posix_testsuite/include/posixtest.h index d1b29848..01d9ef2a 100755 --- a/testcases/open_posix_testsuite/include/posixtest.h +++ b/testcases/open_posix_testsuite/include/posixtest.h @@ -20,9 +20,29 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) #endif -#define PTS_ATTRIBUTE_NORETURN __attribute__((noreturn)) -#define PTS_ATTRIBUTE_UNUSED __attribute__((unused)) -#define PTS_ATTRIBUTE_UNUSED_RESULT __attribute__((warn_unused_result)) +/* __attribute__ is a non portable gcc extension */ +/* TODO: Add support for C23 attributes */ +#if defined __has_attribute +# if __has_attribute(noreturn) +# define PTS_ATTRIBUTE_NORETURN __attribute__((noreturn)) +# endif +# if __has_attribute(unused) +# define PTS_ATTRIBUTE_UNUSED __attribute__((unused)) +# endif +# if __has_attribute(warn_unused_result) +# define PTS_ATTRIBUTE_UNUSED_RESULT __attribute__((warn_unused_result)) +# endif +#endif + +#ifndef PTS_ATTRIBUTE_NORETURN +# define PTS_ATTRIBUTE_NORETURN +#endif +#ifndef PTS_ATTRIBUTE_UNUSED +# define PTS_ATTRIBUTE_UNUSED +#endif +#ifndef PTS_ATTRIBUTE_UNUSED_RESULT +# define PTS_ATTRIBUTE_UNUSED_RESULT +#endif #define PTS_WRITE_MSG(msg) do { \ if (write(STDOUT_FILENO, msg, sizeof(msg) - 1)) { \ diff --git a/testcases/realtime/Makefile b/testcases/realtime/Makefile index a7491654..4d783b01 100755 --- a/testcases/realtime/Makefile +++ b/testcases/realtime/Makefile @@ -22,74 +22,14 @@ top_srcdir ?= ../.. -# Override these variables to use non-system available tools. -ACLOCAL ?= aclocal -AUTOCONF ?= autoconf -AUTOHEADER ?= autoheader -AUTOMAKE ?= automake - include $(top_srcdir)/include/mk/env_pre.mk -# Ensure that this error / warning only applies for build targets. -# -# NOTE (garrcoop): this criterium should be in-sync with include/mk/env_pre.mk -# (minus help as that's only invoked in the top-level Makefile). -ifeq ($(filter autotools %clean .gitignore gitignore.%,$(MAKECMDGOALS)),) -include $(abs_srcdir)/config.mk -endif -include $(top_srcdir)/include/mk/gitignore.mk LIBDIR := lib -FILTER_OUT_DIRS := $(LIBDIR) m4 +FILTER_OUT_DIRS := $(LIBDIR) LIB := $(LIBDIR)/librealtime.a -# START autotools junk -AUTOGENERATED_FILES = \ - m4/Makefile - -AUTOMAKE_FILES := config.guess config.sub install-sh missing stamp-h1 - -.PHONY: autotools aclocal autoconf autoheader automake -autotools: aclocal autoconf autoheader automake - -.PHONY: ac-clean ac-distclean ac-maintainer-clean -ac-clean:: - $(RM) -rf autom4te.cache - $(RM) -f aclocal.m4 config.log config.status - $(RM) -f include/realtime_config.h include/stamp-h1 - -ac-distclean:: ac-clean -ac-maintainer-clean:: ac-distclean - $(RM) -f aclocal.m4 configure $(AUTOMAKE_FILES) m4/Makefile.in - $(RM) -f include/realtime_config.h.in - -aclocal.m4: - $(ACLOCAL) -I $(abs_srcdir)/m4 - -autoconf: configure - -include/realtime_config.h.in: configure.ac $(wildcard m4/*.m4) aclocal.m4 - $(AUTOHEADER) - -configure: configure.ac aclocal.m4 - $(AUTOCONF) -automake: $(AUTOMAKE_FILES) -$(AUTOMAKE_FILES): m4/Makefile.in -m4/Makefile.in: m4/Makefile.am aclocal.m4 - $(AUTOMAKE) -c -a - -.PHONY: autoheader -autoheader: include/realtime_config.h.in - $(AUTOHEADER) - -distclean:: %: clean ac-distclean - $(RM) -f $(AUTOGENERATED_FILES) - -maintainer-clean:: distclean ac-maintainer-clean - -$(AUTOGENERATED_FILES): $(top_builddir)/config.status - $(SHELL) $^ - -# END autotools junk +INSTALL_DIR := $(srcdir) +INSTALL_TARGETS := run.sh $(LIBDIR): mkdir -p "$@" diff --git a/testcases/realtime/configure.ac b/testcases/realtime/configure.ac deleted file mode 100755 index 6f50f149..00000000 --- a/testcases/realtime/configure.ac +++ /dev/null @@ -1,42 +0,0 @@ -AC_PREREQ(2.61) -AC_INIT([realtime],[realtime-20090930],[ltp-results@lists.sourceforge.net]) -AC_CONFIG_AUX_DIR([.]) -AM_INIT_AUTOMAKE -AC_CONFIG_HEADERS([include/realtime_config.h]) -AC_CONFIG_FILES([ \ - m4/Makefile \ -]) -AC_CHECK_HEADERS_ONCE([ \ - pthread.h \ -]) -AC_CANONICAL_HOST - -AC_PROG_CC -# <= autoconf 2.61 doesn't have AC_PROG_AR, but 2.63 has it. Not sure about -# 2.62. -AC_DEFUN([AC_PROG_AR], [AC_CHECK_TOOL(AR, ar, :)]) -AC_PROG_AR -AC_PROG_RANLIB - -AC_MSG_CHECKING([for __sync_add_and_fetch gcc builtin function]) -AC_LINK_IFELSE([AC_LANG_SOURCE([ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include -int main(void) { - char *c; - return __sync_add_and_fetch(c, 1); -}])],[has___sync_add_and_fetch=yes]) -if test "x$has___sync_add_and_fetch" = xyes; then - AC_DEFINE(HAVE___SYNC_ADD_AND_FETCH,1,[Define to 1 if you have the __sync_add_and_fetch gcc builtin function]) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi - -AC_CHECK_LIB([m], [exp10], [AC_DEFINE([HAVE_EXP10], 1, [Define to 1 if you have exp10 function])]) - -REALTIME_CHECK_PRIO_INHERIT - -AC_OUTPUT diff --git a/testcases/realtime/func/async_handler/async_handler.c b/testcases/realtime/func/async_handler/async_handler.c index 70ef56cc..bbe1d9ad 100755 --- a/testcases/realtime/func/async_handler/async_handler.c +++ b/testcases/realtime/func/async_handler/async_handler.c @@ -39,8 +39,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #include #define SIGNAL_PRIO 89 diff --git a/testcases/realtime/func/async_handler/async_handler_jk.c b/testcases/realtime/func/async_handler/async_handler_jk.c index 4d872182..8ce826b7 100755 --- a/testcases/realtime/func/async_handler/async_handler_jk.c +++ b/testcases/realtime/func/async_handler/async_handler_jk.c @@ -41,8 +41,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" // This is the normal priority for an event handler if not specified. #define NORMAL_PRIORITY 43 diff --git a/testcases/realtime/func/async_handler/async_handler_tsc.c b/testcases/realtime/func/async_handler/async_handler_tsc.c index 73d4ee5c..0fe109c5 100755 --- a/testcases/realtime/func/async_handler/async_handler_tsc.c +++ b/testcases/realtime/func/async_handler/async_handler_tsc.c @@ -44,8 +44,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #include "tst_tsc.h" diff --git a/testcases/realtime/func/gtod_latency/gtod_infinite.c b/testcases/realtime/func/gtod_latency/gtod_infinite.c index 676b132c..cee469bd 100755 --- a/testcases/realtime/func/gtod_latency/gtod_infinite.c +++ b/testcases/realtime/func/gtod_latency/gtod_infinite.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include "librttest.h" #include #include #include diff --git a/testcases/realtime/func/gtod_latency/gtod_latency.c b/testcases/realtime/func/gtod_latency/gtod_latency.c index 26ee85f5..8f8174cd 100755 --- a/testcases/realtime/func/gtod_latency/gtod_latency.c +++ b/testcases/realtime/func/gtod_latency/gtod_latency.c @@ -52,8 +52,8 @@ #include #include #include -#include -#include +#include "libstats.h" +#include "librttest.h" #include #define ITERATIONS 10000000 diff --git a/testcases/realtime/func/hrtimer-prio/hrtimer-prio.c b/testcases/realtime/func/hrtimer-prio/hrtimer-prio.c index cbc2343f..5a382f5b 100755 --- a/testcases/realtime/func/hrtimer-prio/hrtimer-prio.c +++ b/testcases/realtime/func/hrtimer-prio/hrtimer-prio.c @@ -42,8 +42,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define DEF_MED_PRIO 60 // (softirqd-hrtimer,98) #define DEF_ITERATIONS 10000 diff --git a/testcases/realtime/func/matrix_mult/matrix_mult.c b/testcases/realtime/func/matrix_mult/matrix_mult.c index c3209234..dfee3827 100755 --- a/testcases/realtime/func/matrix_mult/matrix_mult.c +++ b/testcases/realtime/func/matrix_mult/matrix_mult.c @@ -6,8 +6,6 @@ * Dinakar Guniguntala */ /*\ - * [Description] - * * Compare running sequential matrix multiplication routines * to running them in parallel to judge multiprocessor * performance @@ -16,8 +14,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define MAX_CPUS 8192 #define PRIO 43 diff --git a/testcases/realtime/func/measurement/preempt_timing.c b/testcases/realtime/func/measurement/preempt_timing.c index b84d5469..87286f63 100755 --- a/testcases/realtime/func/measurement/preempt_timing.c +++ b/testcases/realtime/func/measurement/preempt_timing.c @@ -51,7 +51,7 @@ #include #include #include -#include +#include "librttest.h" #include "tst_tsc.h" diff --git a/testcases/realtime/func/measurement/rdtsc-latency.c b/testcases/realtime/func/measurement/rdtsc-latency.c index 3829947b..dba5a90f 100755 --- a/testcases/realtime/func/measurement/rdtsc-latency.c +++ b/testcases/realtime/func/measurement/rdtsc-latency.c @@ -43,7 +43,7 @@ #include #include #include -#include +#include "librttest.h" #include "tst_tsc.h" diff --git a/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load.c b/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load.c index 9b9307a2..386eed98 100755 --- a/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load.c +++ b/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load.c @@ -38,8 +38,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define PRIO_A 63 #define PRIO_B 53 diff --git a/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load_single.c b/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load_single.c index ebbb4613..1e23d883 100755 --- a/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load_single.c +++ b/testcases/realtime/func/periodic_cpu_load/periodic_cpu_load_single.c @@ -38,8 +38,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define HIST_BUCKETS 100 diff --git a/testcases/realtime/func/pi-tests/test-skeleton.c b/testcases/realtime/func/pi-tests/test-skeleton.c index 7816cf87..816076e0 100755 --- a/testcases/realtime/func/pi-tests/test-skeleton.c +++ b/testcases/realtime/func/pi-tests/test-skeleton.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include "librttest.h" void usage(void) { diff --git a/testcases/realtime/func/pi-tests/testpi-0.c b/testcases/realtime/func/pi-tests/testpi-0.c index fd8ad071..09308238 100755 --- a/testcases/realtime/func/pi-tests/testpi-0.c +++ b/testcases/realtime/func/pi-tests/testpi-0.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include "librttest.h" void usage(void) { diff --git a/testcases/realtime/func/pi-tests/testpi-1.c b/testcases/realtime/func/pi-tests/testpi-1.c index 3dbe292d..8acf215b 100755 --- a/testcases/realtime/func/pi-tests/testpi-1.c +++ b/testcases/realtime/func/pi-tests/testpi-1.c @@ -44,7 +44,7 @@ #include #include #include -#include +#include "librttest.h" pthread_barrier_t barrier; diff --git a/testcases/realtime/func/pi-tests/testpi-2.c b/testcases/realtime/func/pi-tests/testpi-2.c index 3f7185e8..1752d0af 100755 --- a/testcases/realtime/func/pi-tests/testpi-2.c +++ b/testcases/realtime/func/pi-tests/testpi-2.c @@ -45,7 +45,7 @@ #include #include #include -#include +#include "librttest.h" pthread_barrier_t barrier; diff --git a/testcases/realtime/func/pi-tests/testpi-4.c b/testcases/realtime/func/pi-tests/testpi-4.c index e9e0ed6d..cb309e8b 100755 --- a/testcases/realtime/func/pi-tests/testpi-4.c +++ b/testcases/realtime/func/pi-tests/testpi-4.c @@ -45,7 +45,7 @@ #include #include #include -#include +#include "librttest.h" pthread_barrier_t barrier; diff --git a/testcases/realtime/func/pi-tests/testpi-5.c b/testcases/realtime/func/pi-tests/testpi-5.c index a5248641..80c83fa6 100755 --- a/testcases/realtime/func/pi-tests/testpi-5.c +++ b/testcases/realtime/func/pi-tests/testpi-5.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include "librttest.h" pthread_mutex_t child_mutex; void *child_thread(void *arg) @@ -69,8 +69,6 @@ int do_test(int argc, char **argv) pthread_mutexattr_t mutexattr; int retc, protocol; -#if HAS_PRIORITY_INHERIT - if (pthread_mutexattr_init(&mutexattr) != 0) printf("Failed to init mutexattr\n"); @@ -91,9 +89,6 @@ int do_test(int argc, char **argv) join_threads(); return 0; -#else - return 1; -#endif } #include "test-skeleton.c" diff --git a/testcases/realtime/func/pi-tests/testpi-6.c b/testcases/realtime/func/pi-tests/testpi-6.c index 637d3835..d09e6d2d 100755 --- a/testcases/realtime/func/pi-tests/testpi-6.c +++ b/testcases/realtime/func/pi-tests/testpi-6.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include "librttest.h" pthread_mutex_t child_mutex; diff --git a/testcases/realtime/func/pi-tests/testpi-7.c b/testcases/realtime/func/pi-tests/testpi-7.c index 20908b66..49989877 100755 --- a/testcases/realtime/func/pi-tests/testpi-7.c +++ b/testcases/realtime/func/pi-tests/testpi-7.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include "librttest.h" #define HIGH_PRIO 15 #define MED_PRIO 10 diff --git a/testcases/realtime/func/pi_perf/pi_perf.c b/testcases/realtime/func/pi_perf/pi_perf.c index 45cf8ed1..fa87839f 100755 --- a/testcases/realtime/func/pi_perf/pi_perf.c +++ b/testcases/realtime/func/pi_perf/pi_perf.c @@ -47,8 +47,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define LOWPRIO 30 #define HIGHPRIO 40 diff --git a/testcases/realtime/func/prio-preempt/prio-preempt.c b/testcases/realtime/func/prio-preempt/prio-preempt.c index 9bd5e7ad..5dd717d1 100755 --- a/testcases/realtime/func/prio-preempt/prio-preempt.c +++ b/testcases/realtime/func/prio-preempt/prio-preempt.c @@ -59,6 +59,7 @@ * *****************************************************************************/ +#define _GNU_SOURCE #include #include #include @@ -67,7 +68,7 @@ #include #include #include -#include +#include "librttest.h" #define NUM_WORKERS 27 #define CHECK_LIMIT 1 @@ -295,10 +296,10 @@ int main(int argc, char *argv[]) pass_criteria = CHECK_LIMIT; rt_init("hin:", parse_args, argc, argv); - numcpus = sysconf(_SC_NPROCESSORS_ONLN); + numcpus = get_numcpus(); - /* Max no. of busy threads should always be less than/equal the no. of cpus - Otherwise, the box will hang */ + /* Max no. of busy threads should always be less than/equal the no. of + housekeeping cpus. Otherwise, the box will hang */ if (rt_threads == -1 || rt_threads > numcpus) { rt_threads = numcpus; diff --git a/testcases/realtime/func/prio-wake/prio-wake.c b/testcases/realtime/func/prio-wake/prio-wake.c index 8b3bee7b..94eea0f4 100755 --- a/testcases/realtime/func/prio-wake/prio-wake.c +++ b/testcases/realtime/func/prio-wake/prio-wake.c @@ -53,8 +53,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" volatile int running_threads = 0; static int rt_threads = 0; diff --git a/testcases/realtime/func/pthread_kill_latency/pthread_kill_latency.c b/testcases/realtime/func/pthread_kill_latency/pthread_kill_latency.c index dc3dd3e8..48cbffb8 100755 --- a/testcases/realtime/func/pthread_kill_latency/pthread_kill_latency.c +++ b/testcases/realtime/func/pthread_kill_latency/pthread_kill_latency.c @@ -51,8 +51,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define PRIO 89 #define ITERATIONS 10000 diff --git a/testcases/realtime/func/rt-migrate/rt-migrate.c b/testcases/realtime/func/rt-migrate/rt-migrate.c index 97ab604c..2a17628f 100755 --- a/testcases/realtime/func/rt-migrate/rt-migrate.c +++ b/testcases/realtime/func/rt-migrate/rt-migrate.c @@ -67,13 +67,16 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define gettid() syscall(__NR_gettid) #define VERSION_STRING "V 0.4LTP" +#define CLAMP(x, lower, upper) (MIN(upper, MAX(x, lower))) +#define CLAMP_PRIO(prio) CLAMP(prio, prio_min, prio_max) + int nr_tasks; int lfd; @@ -137,7 +140,7 @@ static unsigned long long interval = INTERVAL; static unsigned long long run_interval = RUN_INTERVAL; static unsigned long long max_err = MAX_ERR; static int nr_runs = NR_RUNS; -static int prio_start = PRIO_START; +static int prio_start = PRIO_START, prio_min, prio_max; static int check = 1; static int stop; @@ -284,8 +287,8 @@ static void print_results(void) printf("Parent pid: %d\n", getpid()); for (t = 0; t < nr_tasks; t++) { - printf(" Task %d (prio %d) (pid %ld):\n", t, t + prio_start, - thread_pids[t]); + printf(" Task %d (prio %d) (pid %ld):\n", t, + CLAMP_PRIO(t + prio_start), thread_pids[t]); printf(" Max: %lld us\n", tasks_max[t]); printf(" Min: %lld us\n", tasks_min[t]); printf(" Tot: %lld us\n", tasks_avg[t] * nr_runs); @@ -394,6 +397,13 @@ static void stop_log(int sig) int main(int argc, char **argv) { + /* + * Determine the valid priority range; subtracting one from the + * maximum to reserve the highest prio for main thread. + */ + prio_min = sched_get_priority_min(SCHED_FIFO); + prio_max = sched_get_priority_max(SCHED_FIFO) - 1; + int *threads; long i; int ret; @@ -409,6 +419,10 @@ int main(int argc, char **argv) numcpus = sysconf(_SC_NPROCESSORS_ONLN); nr_tasks = numcpus + 1; } + if (nr_tasks < 0) { + printf("The number of tasks must not be negative.\n"); + exit(EXIT_FAILURE); + } intervals = malloc(sizeof(stats_container_t) * nr_tasks); if (!intervals) @@ -448,7 +462,7 @@ int main(int argc, char **argv) for (i = 0; i < nr_tasks; i++) { threads[i] = create_fifo_thread(start_task, (void *)i, - prio_start + i); + CLAMP_PRIO(prio_start + i)); } /* @@ -460,7 +474,8 @@ int main(int argc, char **argv) /* up our prio above all tasks */ memset(¶m, 0, sizeof(param)); - param.sched_priority = nr_tasks + prio_start; + param.sched_priority = CLAMP(nr_tasks + prio_start, prio_min, + prio_max + 1); if (sched_setscheduler(0, SCHED_FIFO, ¶m)) debug(DBG_WARN, "Warning, can't set priority of main thread!\n"); intv.tv_sec = INTERVAL / NS_PER_SEC; diff --git a/testcases/realtime/func/sched_football/Makefile b/testcases/realtime/func/sched_football/Makefile index 61753f03..aac98eab 100755 --- a/testcases/realtime/func/sched_football/Makefile +++ b/testcases/realtime/func/sched_football/Makefile @@ -1,28 +1,16 @@ -# -# realtime/func/sched_football test suite Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2009, Cisco Systems Inc. +# Copyright (c) Linux Test Project, 2009-2024 +# realtime/func/sched_football test suite Makefile. # Ngie Cooper, September 2009 -# top_srcdir ?= ../../../.. -INSTALL_TARGETS := run_auto.sh include $(top_srcdir)/include/mk/env_pre.mk include $(abs_srcdir)/../../config.mk + +INSTALL_DIR=testcases/bin + +# TODO: integrate properly with LTP library +LDLIBS += -lltp include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/realtime/func/sched_football/run_auto.sh b/testcases/realtime/func/sched_football/run_auto.sh deleted file mode 100755 index 7e0703ac..00000000 --- a/testcases/realtime/func/sched_football/run_auto.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -profile=${1:-default} - -cd $(dirname $0) # Move to test directory -if [ ! $SCRIPTS_DIR ]; then - # assume we're running standalone - export SCRIPTS_DIR=../../scripts/ -fi - -. $SCRIPTS_DIR/setenv.sh - -# Warning: tests args are now set in profiles -$SCRIPTS_DIR/run_c_files.sh $profile sched_football diff --git a/testcases/realtime/func/sched_football/sched_football.c b/testcases/realtime/func/sched_football/sched_football.c index 6f075aea..4465bdde 100755 --- a/testcases/realtime/func/sched_football/sched_football.c +++ b/testcases/realtime/func/sched_football/sched_football.c @@ -1,210 +1,222 @@ -/****************************************************************************** - * - * Copyright © International Business Machines Corp., 2007, 2008 - * - * 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 - * - * NAME - * sched_football.c - * - * DESCRIPTION - * This is a scheduler test that uses a football analogy. - * The premise is that we want to make sure that lower priority threads - * (defensive team). The offense is trying to increment the balls position, - * while the defense is trying to block that from happening. - * And the ref (highest priority thread) will blow the wistle if the - * ball moves. Finally, we have crazy fans (higer prority) that try to - * distract the defense by occasionally running onto the field. - * - * Steps: - * - Create a fixed number of offense threads (lower priority) - * - Create a referee thread (highest priority) - * - Once everyone is on the field, the offense thread increments the - * value of 'the_ball' and yields. The defense thread tries to block - * the ball by never letting the offense players get the CPU (it just - * does a sched_yield). - * - The refree threads wakes up regularly to check if the game is over :) - * - In the end, if the value of 'the_ball' is >0, the test is considered - * to have failed. - * - * USAGE: - * Use run_auto.sh script in current directory to build and run test. - * - * AUTHOR - * John Stultz - * - * HISTORY - * 2006-03-16 Reduced verbosity, non binary failure reporting, removal of - * crazy_fans thread, added game_length argument by Darren Hart. - * 2007-08-01 Remove all thread cleanup in favor of simply exiting.Various - * bugfixes and cleanups. -- Josh Triplett - * 2009-06-23 Simplified atomic startup mechanism, avoiding thundering herd - * scheduling at the beginning of the game. -- Darren Hart - * - *****************************************************************************/ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright © International Business Machines Corp., 2007, 2008 + * Copyright (c) 2024 Petr Vorel + * Author: John Stultz + */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +/*\ + * Scheduler test that uses a football analogy. + * + * The premise is that we want to make sure that lower priority threads + * don't run while we have runnable higher priority threads. + * The offense is trying to increment the balls position, while the + * defense is trying to block that from happening. + * And the ref (highest priority thread) will blow the wistle if the + * ball moves. Finally, we have crazy fans (higer prority) that try to + * distract the defense by occasionally running onto the field. + * + * [Algorithm] + * + * - Create NR_CPU offense threads (lower priority). + * - Create NR_CPU defense threads (mid priority). + * - Create 2*NR_CPU fan threads (high priority). + * - Create a referee thread (highest priority). + * - Once everyone is on the field, the offense thread spins incrementing + * the value of 'the_ball'. The defense thread tries to block 'the_ball' + * by never letting the offense players get the CPU (it just spins). + * The crazy fans sleep a bit, then jump the rail and run across the + * field, disrupting the players on the field. + * - The refree threads wakes up regularly to check if the game is over :). + * - If the value of 'the_ball' is > 0, the test is considered to have failed. + */ + +#include +#include +#include +#include "librttest.h" +#include "tst_test.h" #define DEF_GAME_LENGTH 5 +#define SPIN_TIME_NS 200000000ULL +#define SLEEP_TIME_NS 50000000ULL -/* Here's the position of the ball */ -volatile int the_ball; - +static tst_atomic_t the_ball; static int players_per_team = 0; static int game_length = DEF_GAME_LENGTH; -static atomic_t players_ready; +static tst_atomic_t kickoff_flag; +static tst_atomic_t game_over; -void usage(void) +static char *str_game_length; +static char *str_players_per_team; +static pthread_barrier_t start_barrier; + +/* These are fans running across the field. They're trying to interrupt/distract everyone */ +void *thread_fan(void *arg LTP_ATTRIBUTE_UNUSED) { - rt_help(); - printf("sched_football specific options:\n"); - printf(" -nPLAYERS players per team (defaults to num_cpus)\n"); - printf(" -lGAME_LENGTH game length in seconds (defaults to %d s)\n", - DEF_GAME_LENGTH); -} + prctl(PR_SET_NAME, "crazy_fan", 0, 0, 0); + pthread_barrier_wait(&start_barrier); + /*occasionally wake up and run across the field */ + while (!tst_atomic_load(&game_over)) { + struct timespec start, stop; + nsec_t nsec; -int parse_args(int c, char *v) -{ - - int handled = 1; - switch (c) { - case 'h': - usage(); - exit(0); - case 'n': - players_per_team = atoi(v); - break; - case 'l': - game_length = atoi(v); - break; - default: - handled = 0; - break; + start.tv_sec = 0; + start.tv_nsec = SLEEP_TIME_NS; + clock_nanosleep(CLOCK_MONOTONIC, 0, &start, NULL); + clock_gettime(CLOCK_MONOTONIC, &start); + clock_gettime(CLOCK_MONOTONIC, &stop); + nsec = tst_timespec_diff_ns(stop, start); + while (nsec < SPIN_TIME_NS) { + clock_gettime(CLOCK_MONOTONIC, &stop); + nsec = tst_timespec_diff_ns(stop, start); + } } - return handled; + + return NULL; } /* This is the defensive team. They're trying to block the offense */ -void *thread_defense(void *arg) +void *thread_defense(void *arg LTP_ATTRIBUTE_UNUSED) { - atomic_inc(&players_ready); + prctl(PR_SET_NAME, "defense", 0, 0, 0); + pthread_barrier_wait(&start_barrier); + while (!tst_atomic_load(&kickoff_flag)) + ; + /*keep the ball from being moved */ - while (1) { - sched_yield(); /* let other defenders run */ + while (!tst_atomic_load(&game_over)) { } + return NULL; } /* This is the offensive team. They're trying to move the ball */ -void *thread_offense(void *arg) +void *thread_offense(void *arg LTP_ATTRIBUTE_UNUSED) { - atomic_inc(&players_ready); - while (1) { - the_ball++; /* move the ball ahead one yard */ - sched_yield(); /* let other offensive players run */ + prctl(PR_SET_NAME, "offense", 0, 0, 0); + pthread_barrier_wait(&start_barrier); + while (!tst_atomic_load(&kickoff_flag)) + sched_yield(); + + while (!tst_atomic_load(&game_over)) { + tst_atomic_add_return(1, &the_ball); /* move the ball ahead one yard */ } + return NULL; } -int referee(int game_length) +void referee(int game_length) { struct timeval start, now; int final_ball; - printf("Game On (%d seconds)!\n", game_length); + tst_res(TINFO, "Starting referee thread"); + prctl(PR_SET_NAME, "referee", 0, 0, 0); + tst_res(TINFO, "Starting the game (%d sec)", game_length); + + /* open trace marker early to avoid latency with the first message */ + trace_marker_prep(); gettimeofday(&start, NULL); now = start; /* Start the game! */ - the_ball = 0; + atrace_marker_write("sched_football", "Game_started!"); + pthread_barrier_wait(&start_barrier); + usleep(200000); + + tst_atomic_store(0, &the_ball); + tst_atomic_store(1, &kickoff_flag); + if (tst_check_preempt_rt()) + usleep(20000); + else + usleep(2000000); /* Watch the game */ while ((now.tv_sec - start.tv_sec) < game_length) { sleep(1); gettimeofday(&now, NULL); } + /* Blow the whistle */ - printf("Game Over!\n"); - final_ball = the_ball; - printf("Final ball position: %d\n", final_ball); - return final_ball != 0; + final_ball = tst_atomic_load(&the_ball); + tst_res(TINFO, "Final ball position: %d", final_ball); + + /* Stop the game! */ + tst_atomic_store(1, &game_over); + atrace_marker_write("sched_football", "Game_Over!"); + + TST_EXP_EXPR(final_ball == 0); } -int main(int argc, char *argv[]) +static void do_test(void) { struct sched_param param; int priority; int i; - int result; - setup(); - - rt_init("n:l:h", parse_args, argc, argv); if (players_per_team == 0) - players_per_team = sysconf(_SC_NPROCESSORS_ONLN); + players_per_team = get_numcpus(); - atomic_set(0, &players_ready); - - printf("Running with: players_per_team=%d game_length=%d\n", + tst_res(TINFO, "players_per_team: %d game_length: %d", players_per_team, game_length); + /* total = offense + defense + fans + referee */ + pthread_barrier_init(&start_barrier, NULL, players_per_team * 4 + 1); + /* We're the ref, so set our priority right */ param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 80; sched_setscheduler(0, SCHED_FIFO, ¶m); + tst_atomic_store(0, &kickoff_flag); /* * Start the offense * They are lower priority than defense, so they must be started first. */ priority = 15; - printf("Starting %d offense threads at priority %d\n", + tst_res(TINFO, "Starting %d offense threads at priority %d", players_per_team, priority); for (i = 0; i < players_per_team; i++) create_fifo_thread(thread_offense, NULL, priority); - /* Wait for the offense threads to start */ - while (atomic_get(&players_ready) < players_per_team) - usleep(100); - /* Start the defense */ priority = 30; - printf("Starting %d defense threads at priority %d\n", + tst_res(TINFO, "Starting %d defense threads at priority %d", players_per_team, priority); for (i = 0; i < players_per_team; i++) create_fifo_thread(thread_defense, NULL, priority); - /* Wait for the defense threads to start */ - while (atomic_get(&players_ready) < players_per_team * 2) - usleep(100); + /* Start the crazy fans*/ + priority = 50; + tst_res(TINFO, "Starting %d crazy-fan threads at priority %d", + players_per_team*2, priority); + for (i = 0; i < players_per_team*2; i++) + create_fifo_thread(thread_fan, NULL, priority); - /* Ok, everyone is on the field, bring out the ref */ - printf("Starting referee thread\n"); - result = referee(game_length); - printf("Result: %s\n", result ? "FAIL" : "PASS"); - return result; + referee(game_length); + pthread_barrier_destroy(&start_barrier); } + +static void do_setup(void) +{ + if (tst_parse_int(str_game_length, &game_length, 1, INT_MAX)) + tst_brk(TBROK, "Invalid game length '%s'", str_game_length); + + if (tst_parse_int(str_players_per_team, &players_per_team, 1, INT_MAX)) + tst_brk(TBROK, "Invalid number of players '%s'", str_players_per_team); +} + +static struct tst_test test = { + .test_all = do_test, + .setup = do_setup, + .needs_root = 1, + .options = (struct tst_option[]) { + {"l:", &str_game_length, "Game length in sec (default: " + TST_TO_STR(DEF_GAME_LENGTH) " sec)"}, + {"n:", &str_players_per_team, + "Number of players (default: number of CPU)"}, + {} + }, +}; diff --git a/testcases/realtime/func/sched_jitter/sched_jitter.c b/testcases/realtime/func/sched_jitter/sched_jitter.c index 99895289..d1fba431 100755 --- a/testcases/realtime/func/sched_jitter/sched_jitter.c +++ b/testcases/realtime/func/sched_jitter/sched_jitter.c @@ -47,8 +47,8 @@ #include #include #include -#include -#include +#include "libstats.h" +#include "librttest.h" #define NUMRUNS 1000 #define NUMLOOPS 1000000 diff --git a/testcases/realtime/func/sched_latency/sched_latency.c b/testcases/realtime/func/sched_latency/sched_latency.c index d19d5889..49d253b3 100755 --- a/testcases/realtime/func/sched_latency/sched_latency.c +++ b/testcases/realtime/func/sched_latency/sched_latency.c @@ -49,8 +49,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define PRIO 89 //#define PERIOD 17*NS_PER_MS diff --git a/testcases/realtime/func/thread_clock/tc-2.c b/testcases/realtime/func/thread_clock/tc-2.c index f994d40d..323fbd12 100755 --- a/testcases/realtime/func/thread_clock/tc-2.c +++ b/testcases/realtime/func/thread_clock/tc-2.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include "librttest.h" #define NS_PER_SEC 1000000000 #define THRESHOLD 0.5 /* 500 milliseconds */ diff --git a/testcases/realtime/include/librttest.h b/testcases/realtime/include/librttest.h index 8e342186..39538c47 100755 --- a/testcases/realtime/include/librttest.h +++ b/testcases/realtime/include/librttest.h @@ -56,7 +56,6 @@ #include #include #include "list.h" -#include "realtime_config.h" extern void setup(void); extern void cleanup(int i); @@ -113,15 +112,9 @@ extern double pass_criteria; */ static inline int atomic_add(int i, atomic_t *v) { - /* XXX (garrcoop): only available in later versions of gcc */ -#if HAVE___SYNC_ADD_AND_FETCH return __sync_add_and_fetch(&v->counter, i); -#else - printf("%s: %s\n", __func__, strerror(ENOSYS)); - exit(1); - return -1; -#endif } + /* atomic_inc: atomically increment the integer passed by reference */ static inline int atomic_inc(atomic_t *v) @@ -349,4 +342,21 @@ void latency_trace_stop(void); */ void latency_trace_print(void); +/* trace_marker_prep: open trace_marker file (optional) + */ +void trace_marker_prep(void); + +/* trace_marker_write: write buf to trace_marker. + * Will open trace_marker file if not already open + */ +int trace_marker_write(char *buf, int len); + +/* atrace_marker_write: write atrace format message to trace_marker + */ +int atrace_marker_write(char *tag, char *msg); + +/* get_numcpus: get the number of cpus accessible to the current process + */ +int get_numcpus(void); + #endif /* LIBRTTEST_H */ diff --git a/testcases/realtime/include/list.h b/testcases/realtime/include/list.h index f87d602e..01159e34 100755 --- a/testcases/realtime/include/list.h +++ b/testcases/realtime/include/list.h @@ -39,6 +39,8 @@ #ifndef _LINUX_LIST_H #define _LINUX_LIST_H +#include + /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses @@ -241,7 +243,6 @@ static inline void list_splice_init(struct list_head *list, } } -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) /** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. diff --git a/testcases/realtime/lib/librttest.c b/testcases/realtime/lib/librttest.c index eaa623b7..21ea57de 100755 --- a/testcases/realtime/lib/librttest.c +++ b/testcases/realtime/lib/librttest.c @@ -40,8 +40,8 @@ * *****************************************************************************/ -#include -#include +#include "librttest.h" +#include "libstats.h" #include #include @@ -591,7 +591,6 @@ void *busy_work_us(int us) void init_pi_mutex(pthread_mutex_t * m) { -#if HAS_PRIORITY_INHERIT pthread_mutexattr_t attr; int ret; int protocol; @@ -614,9 +613,6 @@ void init_pi_mutex(pthread_mutex_t * m) if ((ret = pthread_mutex_init(m, &attr)) != 0) { printf("Failed to init mutex: %d (%s)\n", ret, strerror(ret)); } -#endif - - /* FIXME: does any of this need to be destroyed ? */ } /* Write the entirety of data. Complain if unable to do so. */ @@ -732,3 +728,49 @@ void latency_trace_print(void) { read_and_print("/proc/latency_trace", STDOUT_FILENO); } + +static int trace_marker_fd = -1; + +void trace_marker_prep(void) +{ + if (trace_marker_fd != -1) + return; + trace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_RDWR, 0); +} + +int trace_marker_write(char *buf, int len) +{ + if (trace_marker_fd == -1) + trace_marker_prep(); + + if (trace_marker_fd < 0) + return -1; + + return write(trace_marker_fd, buf, len); +} + +#define TRACE_BUF_LEN 256 +static char trace_buf[TRACE_BUF_LEN]; + +int atrace_marker_write(char *tag, char *msg) +{ + /* Uses atrace format perfetto can visualize */ + snprintf(trace_buf, TRACE_BUF_LEN, "I|%i|%s: %s\n", getpid(), tag, msg); + return trace_marker_write(trace_buf, + strnlen(trace_buf, TRACE_BUF_LEN)); +} + +int get_numcpus(void) +{ + long numcpus_conf = sysconf(_SC_NPROCESSORS_CONF); + size_t size = CPU_ALLOC_SIZE(numcpus_conf); + int cpu_count; + cpu_set_t *cpuset = CPU_ALLOC(numcpus_conf); + + CPU_ZERO_S(size, cpuset); + /* Get the number of cpus accessible to the current process */ + sched_getaffinity(0, size, cpuset); + cpu_count = CPU_COUNT_S(size, cpuset); + CPU_FREE(cpuset); + return cpu_count; +} diff --git a/testcases/realtime/lib/libstats.c b/testcases/realtime/lib/libstats.c index f12d618f..52b0cca6 100755 --- a/testcases/realtime/lib/libstats.c +++ b/testcases/realtime/lib/libstats.c @@ -43,14 +43,8 @@ #include #include #include -#include -#include - -#include "../include/realtime_config.h" - -#ifndef HAVE_EXP10 -# define exp10(x) (exp((x) * log(10))) -#endif +#include "libstats.h" +#include "librttest.h" int save_stats = 0; diff --git a/testcases/realtime/m4/.gitignore b/testcases/realtime/m4/.gitignore deleted file mode 100755 index 5fc607b9..00000000 --- a/testcases/realtime/m4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/Makefile diff --git a/testcases/realtime/m4/GNUmakefile b/testcases/realtime/m4/GNUmakefile deleted file mode 100755 index 2a5b4c61..00000000 --- a/testcases/realtime/m4/GNUmakefile +++ /dev/null @@ -1,37 +0,0 @@ -# -# m4 Makefile. -# -# Copyright (C) 2009, Cisco Systems Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Ngie Cooper, July 2009 -# - -top_srcdir ?= ../../.. - -include $(top_srcdir)/include/mk/env_pre.mk - -M4MACROS := $(notdir $(wildcard *.m4)) - -INSTALL_DIR := $(datarootdir)/aclocal - -INSTALL_MODE := 00644 - -INSTALL_TARGETS := $(M4_MACROS) - -MAKE_TARGETS := - -include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/realtime/m4/Makefile.am b/testcases/realtime/m4/Makefile.am deleted file mode 100755 index 87c304b4..00000000 --- a/testcases/realtime/m4/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -# bogus makefile to appease automake diff --git a/testcases/realtime/m4/check.m4 b/testcases/realtime/m4/check.m4 deleted file mode 100755 index d04a2cc7..00000000 --- a/testcases/realtime/m4/check.m4 +++ /dev/null @@ -1,14 +0,0 @@ -AC_DEFUN([REALTIME_CHECK_PRIO_INHERIT],[ -AC_MSG_CHECKING([for PTHREAD_PRIO_INHERIT]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include ]], [[int main(void) { - pthread_mutexattr_t attr; - return pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); -}]])],[has_priority_inherit="yes"],[]) -if test "x$has_priority_inherit" = "xyes" ; then - AC_DEFINE(HAS_PRIORITY_INHERIT,1,[Define to 1 if you have PTHREAD_PRIO_INHERIT]) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi -]) diff --git a/testcases/realtime/perf/latency/pthread_cond_latency.c b/testcases/realtime/perf/latency/pthread_cond_latency.c index d0048d5e..c1b64b20 100755 --- a/testcases/realtime/perf/latency/pthread_cond_latency.c +++ b/testcases/realtime/perf/latency/pthread_cond_latency.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include "librttest.h" pthread_mutex_t child_mutex = PTHREAD_MUTEX_INITIALIZER; volatile int child_waiting = 0; diff --git a/testcases/realtime/perf/latency/pthread_cond_many.c b/testcases/realtime/perf/latency/pthread_cond_many.c index 9c6a8972..2bc9c1ed 100755 --- a/testcases/realtime/perf/latency/pthread_cond_many.c +++ b/testcases/realtime/perf/latency/pthread_cond_many.c @@ -44,8 +44,8 @@ #include #include #include -#include -#include +#include "librttest.h" +#include "libstats.h" #define PASS_US 100 pthread_mutex_t child_mutex; volatile int *child_waiting = NULL; diff --git a/testcases/realtime/stress/pi-tests/lookup_pi_state.c b/testcases/realtime/stress/pi-tests/lookup_pi_state.c index 83781d80..1799892a 100755 --- a/testcases/realtime/stress/pi-tests/lookup_pi_state.c +++ b/testcases/realtime/stress/pi-tests/lookup_pi_state.c @@ -34,7 +34,7 @@ *****************************************************************************/ #include -#include +#include "librttest.h" #define NUM_SLAVES 20 #define SLAVE_PRIO 89 diff --git a/testcases/realtime/stress/pi-tests/testpi-3.c b/testcases/realtime/stress/pi-tests/testpi-3.c index 1c3d4445..8a3e7c73 100755 --- a/testcases/realtime/stress/pi-tests/testpi-3.c +++ b/testcases/realtime/stress/pi-tests/testpi-3.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include "librttest.h" void usage(void) { @@ -365,7 +365,6 @@ int main(int argc, char *argv[]) printf("Start %s\n", argv[0]); -#if HAS_PRIORITY_INHERIT if (!nopi) { pthread_mutexattr_t mutexattr; int protocol; @@ -386,7 +385,6 @@ int main(int argc, char *argv[]) printf("Failed to init mutex: %d\n", retc); } } -#endif startThread(&arg1); startThread(&arg2); diff --git a/testscripts/autofs1.sh b/testscripts/autofs1.sh deleted file mode 100755 index 324c4db9..00000000 --- a/testscripts/autofs1.sh +++ /dev/null @@ -1,273 +0,0 @@ -#!/bin/bash - - -############################################################## -# -# Copyright (c) International Business Machines Corp., 2003 -# -# 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 -# -# FILE : autofs1.sh -# USAGE : autofs1.sh -# -# DESCRIPTION : A script that will test autofs on Linux system. -# REQUIREMENTS: -# 1) System with a floppy device with a floppy disk in it. -# 2) A spare (scratch) disk partition of 100MB or larger. -# -# HISTORY : -# 06/11/2003 Prakash Narayana (prakashn@us.ibm.com) -# 08/01/2005 Michael Reed (mreed10@us.ibm.com) -# - Added an check to see if a directory exists -# - This prevents unnessary failures -# - Correction to an echo statement -# - Added an additional error message if a floppy disk is not present -# -# CODE COVERAGE: -# 41.46% - fs/autofs/dirhash.c -# 33.33% - fs/autofs/init.c -# 27.70% - fs/autofs/inode.c -# 38.16% - fs/autofs/root.c -# 0.00% - fs/autofs/symlink.c -# 43.40% - fs/autofs/waitq.c -# -############################################################## - - -############################################################## -# -# Make sure that uid=root is running this script. -# Validate the command line argument as a block special device. -# Make sure that autofs package has been installed. -# Make sure that autofs module is built into the kernel or loaded. -# -############################################################## - -if [ $UID != 0 ] -then - echo "FAILED: Must have root access to execute this script" - exit 1 -fi - -if [ $# != 1 ] -then - echo "FAILED: Usage $0 " - echo "Example: $0 /dev/hdc1" - exit 1 -else - disk_partition=$1 - if [ ! -b $disk_partition ] - then - echo "FAILED: Usage $0 " - exit 1 - fi - mkfs -t ext2 $disk_partition -fi - -rpm -q -a | grep autofs -if [ $? != 0 ] -then - echo "FAILED: autofs package is not installed" - exit 1 -fi - -grep autofs /proc/filesystems -if [ $? != 0 ] -then - echo "FAILED: autofs module is not built into the kernel or loaded" - exit 1 -fi - - -############################################################## -# -# Pick the floppy device name from /etc/fstab -# Format (mkfs -t ext2) the floppy to ext2 file system -# Create the /etc/auto.master -# Create the /etc/auto.media -# Create /AUTOFS directory. -# -############################################################## - -floppy_dev=`grep floppy /etc/fstab | awk '{print $1}'` - -echo "Found floppy device:$floppy_dev" - -if [ $floppy_dev != "" ] -then - /sbin/mkfs -t ext2 $floppy_dev - if [ $? != 0 ] - then - echo "FAILED: mkfs -t ext2 $floppy_dev failed" - echo "Insert a disk into the floppy drive" - exit 1 - fi -fi - -if [ ! -d /AUTOFS ] -then - mkdir -m 777 /AUTOFS -fi - -echo "/AUTOFS/MEDIA /etc/auto.media" > /etc/auto.master -echo "floppy -fstype=ext2 :$floppy_dev" > /etc/auto.media - - -############################################################## -# -# Verify that "/etc/init.d/autofs start|restart|stop|status|reload" -# command works. -# -# If fails, cleanup and exit. -# -############################################################## - -/etc/init.d/autofs start -if [ $? != 0 ] -then - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs start"" - exit 1 -fi -echo "Resuming test, please wait..." -sleep 15 - -/etc/init.d/autofs stop -if [ $? != 0 ] -then - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs stop"" - exit 1 -else - /etc/init.d/autofs start -fi -echo "Resuming test, please wait..." -sleep 15 - -/etc/init.d/autofs restart -if [ $? != 0 ] -then - /etc/init.d/autofs stop - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs restart"" - exit 1 -fi -echo "Resuming test, please wait..." -sleep 15 - -/etc/init.d/autofs status -if [ $? != 0 ] -then - /etc/init.d/autofs stop - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs status"" - exit 1 -fi - -/etc/init.d/autofs reload -if [ $? != 0 ] -then - /etc/init.d/autofs stop - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs reload"" - exit 1 -fi - - -############################################################## -# -# Tryout some error code paths by: -# (1) Write into automount directory -# (2) Remove automount parent directory -# (3) Automount the floppy disk -# (4) Hit automounter timeout by sleep 60; then wakeup with error -# condition. -# -############################################################## - -echo "forcing error paths and conditions..." - -mkdir /AUTOFS/MEDIA/mydir >/dev/null 2>&1 -rm -rf /AUTOFS >/dev/null 2>&1 - -mkdir /AUTOFS/MEDIA/floppy/test -cp /etc/auto.master /etc/auto.media /AUTOFS/MEDIA/floppy/test -sync; sync -echo "Resuming test, please wait..." -sleep 60 -mkdir /AUTOFS/MEDIA/mydir >/dev/null 2>&1 -rm -rf /AUTOFS >/dev/null 2>&1 - - -############################################################## -# -# Add an entry to the /etc/auto.master and reload. -# -############################################################## - -echo "/AUTOFS/DISK /etc/auto.disk" >> /etc/auto.master -echo "disk -fstype=auto,rw,sync :$disk_partition " > /etc/auto.disk -/etc/init.d/autofs reload -echo "Resuming test, please wait..." -sleep 30 - - - -mkdir /AUTOFS/DISK/disk/test -cp /etc/auto.master /etc/auto.media /AUTOFS/DISK/disk/test -sync; sync -echo "Resuming test, please wait..." -sleep 60 - - -if [ -e /AUTOFS/DISK/disk/test ]; then - cd /AUTOFS/DISK/disk/test - umount /AUTOFS/DISK/disk/ >/dev/null 2>&1 - if [ $? = 0 ] - then - /etc/init.d/autofs stop - rm -rf /etc/auto.master /etc/auto.media /etc/auto.disk /AUTOFS - echo "FAILED: unmounted a busy file system!" - exit 1 - fi - cd - umount /AUTOFS/DISK/disk/ -if [ $? != 0 ] - then - /etc/init.d/autofs stop - rm -rf /etc/auto.master /etc/auto.media /etc/auto.disk /AUTOFS - echo "FAILED: Could not unmount automounted file system" - exit 1 - fi -fi -# -# Mount the disk partition somewhere else and then reference automount -# point for disk partition. -# -mount -t ext2 $disk_partition /mnt/ -ls -l /AUTOFS/DISK/disk -umount /mnt - - -####################################################### -# -# Just before exit, stop autofs and cleanup. -# -####################################################### - -/etc/init.d/autofs stop -rm -rf /etc/auto.master /etc/auto.media /etc/auto.disk /AUTOFS -echo "PASSED: $0 passed!" -exit 0 diff --git a/testscripts/autofs4.sh b/testscripts/autofs4.sh deleted file mode 100755 index 00119a60..00000000 --- a/testscripts/autofs4.sh +++ /dev/null @@ -1,259 +0,0 @@ -#!/bin/bash - - -############################################################## -# -# Copyright (c) International Business Machines Corp., 2003 -# -# 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 -# -# FILE : autofs4.sh -# USAGE : autofs4.sh -# -# DESCRIPTION : A script that will test autofs on Linux system. -# REQUIREMENTS: -# 1) System with a floppy device with a floppy disk in it. -# 2) A spare (scratch) disk partition of 100MB or larger. -# -# HISTORY : -# 06/11/2003 Prakash Narayana (prakashn@us.ibm.com) -# -# CODE COVERAGE: 26.8% - fs/autofs4 (Total Coverage) -# -# 24.1% - fs/autofs4/expire.c -# 33.3% - fs/autofs4/init.c -# 24.0% - fs/autofs4/inode.c -# 29.9% - fs/autofs4/root.c -# 0.0% - fs/autofs4/symlink.c -# 29.1% - fs/autofs4/waitq.c -# -############################################################## - - -############################################################## -# -# Make sure that uid=root is running this script. -# Validate the command line argument as a block special device. -# Make sure that autofs package has been installed. -# Make sure that autofs module is built into the kernel or loaded. -# -############################################################## - -if [ $UID != 0 ] -then - echo "FAILED: Must have root access to execute this script" - exit 1 -fi - -if [ $# != 1 ] -then - echo "FAILED: Usage $0 " - exit 1 -else - disk_partition=$1 - if [ ! -b $disk_partition ] - then - echo "FAILED: Usage $0 " - exit 1 - fi - mkfs -t ext2 $disk_partition >/dev/null 2>&1 -fi - -rpm -q -a | grep autofs >/dev/null 2>&1 -if [ $? != 0 ] -then - echo "FAILED: autofs package is not installed" - exit 1 -fi - -grep autofs /proc/filesystems >/dev/null 2>&1 -if [ $? != 0 ] -then - echo "FAILED: autofs module is not built into the kernel or loaded" - exit 1 -fi - - -############################################################## -# -# Pick the floppy device name from /etc/fstab -# Format (mkfs -t ext2) the floppy to ext2 file system -# Create the /etc/auto.master -# Create the /etc/auto.media -# Create /AUTOFS directory. -# -############################################################## - -floppy_dev=`grep floppy /etc/fstab | awk '{print $1}'` - -if [ $floppy_dev != "" ] -then - /sbin/mkfs -t ext2 $floppy_dev >/dev/null 2>&1 - if [ $? != 0 ] - then - echo "FAILED: mkfs -t ext2 $floppy_dev failed" - exit 1 - fi -fi - -if [ ! -d /AUTOFS ] -then - mkdir -m 755 /AUTOFS -fi - -echo "/AUTOFS/MEDIA /etc/auto.media " > /etc/auto.master -echo "floppy -fstype=ext2 :$floppy_dev" > /etc/auto.media - - -############################################################## -# -# Verify that "/etc/init.d/autofs start|restart|stop|status|reload" -# command works. -# -# If fails, cleanup and exit. -# -############################################################## - -/etc/init.d/autofs start >/dev/null 2>&1 -if [ $? != 0 ] -then - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs start"" - exit 1 -fi -echo "Resuming test, please wait..." -sleep 15 - -/etc/init.d/autofs stop >/dev/null 2>&1 -if [ $? != 0 ] -then - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs stop"" - exit 1 -else - /etc/init.d/autofs start >/dev/null 2>&1 -fi -sleep 15 - -/etc/init.d/autofs restart >/dev/null 2>&1 -if [ $? != 0 ] -then - /etc/init.d/autofs stop >/dev/null 2>&1 - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs restart"" - exit 1 -fi -echo "Resuming test, please wait..." -sleep 15 - -/etc/init.d/autofs status >/dev/null 2>&1 -if [ $? != 0 ] -then - /etc/init.d/autofs stop >/dev/null 2>&1 - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs status"" - exit 1 -fi - -/etc/init.d/autofs reload >/dev/null 2>&1 -if [ $? != 0 ] -then - /etc/init.d/autofs stop >/dev/null 2>&1 - rm -rf /etc/auto.master /etc/auto.media /AUTOFS - echo "FAILED: "/etc/init.d/autofs reload"" - exit 1 -fi - - -############################################################## -# -# Tryout some error code paths by: -# (1) Write into automount directory -# (2) Remove automount parent directory -# (3) Automount the floppy disk -# (4) Hit automounter timeout by sleep 60; then wakeup with error -# condition. -# -############################################################## - -mkdir /AUTOFS/MEDIA/mydir >/dev/null 2>&1 -rm -rf /AUTOFS >/dev/null 2>&1 - -mkdir /AUTOFS/MEDIA/floppy/test -cp /etc/auto.master /etc/auto.media /AUTOFS/MEDIA/floppy/test -sync; sync -echo "Resuming test, please wait..." -sleep 60 -mkdir /AUTOFS/MEDIA/mydir >/dev/null 2>&1 -rm -rf /AUTOFS >/dev/null 2>&1 - - -############################################################## -# -# Add an entry to the /etc/auto.master and reload. -# -############################################################## - -echo "/AUTOFS/DISK /etc/auto.disk " >> /etc/auto.master -echo "disk -fstype=ext2 :$disk_partition " > /etc/auto.disk -/etc/init.d/autofs reload >/dev/null 2>&1 -echo "Resuming test, please wait..." -sleep 30 - -mkdir /AUTOFS/DISK/disk/test -cp /etc/auto.master /etc/auto.media /AUTOFS/DISK/disk/test -sync; sync -echo "Resuming test, please wait..." -sleep 60 - -cd /AUTOFS/DISK/disk/test -umount /AUTOFS/DISK/disk/ >/dev/null 2>&1 -if [ $? = 0 ] -then - /etc/init.d/autofs stop >/dev/null 2>&1 - rm -rf /etc/auto.master /etc/auto.media /etc/auto.disk /AUTOFS - echo "FAILED: unmounted a busy file system!" - exit 1 -fi -cd - -umount /AUTOFS/DISK/disk/ >/dev/null 2>&1 -if [ $? != 0 ] -then - /etc/init.d/autofs stop >/dev/null 2>&1 - rm -rf /etc/auto.master /etc/auto.media /etc/auto.disk /AUTOFS - echo "FAILED: Could not unmount automounted file system" - exit 1 -fi - -# -# Mount the disk partition somewhere else and then reference automount -# point for disk partition. -# -mount -t ext2 $disk_partition /mnt/ -ls -l /AUTOFS/DISK/disk -umount /mnt - - -####################################################### -# -# Just before exit, stop autofs and cleanup. -# -####################################################### - -/etc/init.d/autofs stop >/dev/null 2>&1 -rm -rf /etc/auto.master /etc/auto.media /etc/auto.disk /AUTOFS -echo "PASSED: $0 passed!" -exit 0 diff --git a/testscripts/load_stress_all_kernel_modules.sh b/testscripts/load_stress_all_kernel_modules.sh deleted file mode 100755 index 6e67b154..00000000 --- a/testscripts/load_stress_all_kernel_modules.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -################################################################################ -## ## -## Copyright (c) International Business Machines Corp., 2009 ## -## ## -## 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 ## -## ## -################################################################################ -# ## -# File : load_stress_all_kernel_modules.sh ## -# ## -# Description: Try to load all the modules present in the system, installed ## -# both during Distro installation, or, custom kernel build. ## -# ## -# Author: Subrata Modak ## -################################################################################ - -for module in `modprobe -l | tr '\n' ' '` - do - insert_module=`basename $module .ko` - modprobe -v $insert_module -done - diff --git a/testscripts/ltp-scsi_debug.sh b/testscripts/ltp-scsi_debug.sh deleted file mode 100755 index 37853002..00000000 --- a/testscripts/ltp-scsi_debug.sh +++ /dev/null @@ -1,218 +0,0 @@ -#!/bin/bash - -# Check if scsi_debug module was built or not -export kernel=$(uname -r) - -ls /lib/modules/$kernel/kernel/drivers/scsi | grep scsi_debug > /dev/null 2>&1 -if [ $? -ne 0 ]; -then - echo "scsi_debug was not built in the kernel as a module" - echo "Build scsi_debug as a module first before running the test" -fi - -# Unload scsi_debug moudle if it was already loaded: -lsmod | grep scsi_debug > /dev/null 2>&1 -if [ $? -eq 0 ]; -then - echo "The scsi_debug module was already loaded, unload it before test..." - rmmod -f scsi_debug -fi -lsmod | grep scsi_debug > /dev/null 2>&1 -if [ $? -eq 0 ]; -then - echo "The scsi_debug module was not unloaded - unload error" -else - echo "The scsi_debug module was unloaded successfully" - echo "start testing..." -fi - -orig_count=$(cat /proc/partitions | wc -l) - -echo " Load the scsi_debug module..." -modprobe scsi_debug -if [ $? -ne 0 ]; -then - echo "Can't load scsi_debug modules" - exit -fi - -echo "Check if scsi_debug was loaded..." -lsmod | grep scsi_debug > /dev/null 2>&1 -if [ $? -eq 0 ]; -then - echo "scsi_debug module was loaded successfully" -else - echo "scsi_debug module was not loaded" - exit -fi - - -echo "Remove the scsi_debug device..." -dev_name=$(ls /proc/scsi/scsi_debug) -# echo $dev_name -rm_dev=$dev_name:0:0:0 -# echo $rm_dev -echo 1 > /sys/class/scsi_device/$rm_dev/device/delete - -echo "Check if the scsi_debug device was removed..." -ls /sys/class/scsi_device | grep $rm_dev > /dev/null 2>&1 -if [ $? -eq 0 ]; -then - echo "The device was not removed - remove error" -else - echo "The device $dev_name was removed successfully" -fi - -echo "Add the device back..." -echo "0 0 0" > /sys/class/scsi_host/host$dev_name/scan -ls /sys/class/scsi_device | grep $rm_dev > /dev/null 2>&1 -if [ $? -ne 0 ]; -then - echo "The device was not added - add device error" -else - echo "The device $dev_name was added back successfully" -fi - -echo "Add a new host..." -echo 1 > /sys/bus/pseudo/drivers/scsi_debug/add_host -num_host=$(cat /sys/bus/pseudo/drivers/scsi_debug/add_host) -if [ $num_host -ne 2 ]; -then - echo "The new host was not added - add host error" -else - echo "The new host was added successfully" -fi - -echo "Romove hosts..." -echo -2 > /sys/bus/pseudo/drivers/scsi_debug/add_host -num_host=$(cat /sys/bus/pseudo/drivers/scsi_debug/add_host) -if [ $num_host -ne 0 ]; -then - echo "The hosts were not removed - remove hosts error" -else - echo "The hosts were removed successfully" -fi - -echo "Unload scsi_debug moudle..." -rmmod -f scsi_debug -lsmod | grep scsi_debug > /dev/null 2>&1 -if [ $? -eq 0 ]; -then - echo "The scsi_debug module was not unloaded - unload error" -else - echo "The scsi_debug module was unloaded successfully" -fi - -echo "Load scsi_debug with multiple hosts..." -modprobe scsi_debug max_luns=2 num_tgts=2 add_host=2 dev_size_mb=20 -lsmod | grep scsi_debug > /dev/null 2>&1 -if [ $? -eq 0 ]; -then - echo "The multiple scsi_debug modules were loaded successfully" -else - echo "The multiple scsi_debug modules were not loaded - load error" -fi - -echo "Check if modules were loaded as required by premeters..." -max_luns=$(cat /sys/bus/pseudo/drivers/scsi_debug/max_luns) -add_host=$(cat /sys/bus/pseudo/drivers/scsi_debug/add_host) -num_tgts=$(cat /sys/bus/pseudo/drivers/scsi_debug/num_tgts) -# echo "max_lunx is $max_luns" -# echo "add_host is $add_host" -# echo "num_tgts is $num_tgts" - -premeter_err_ct=0; - -if [ $max_luns -ne 2 ]; -then - echo "max_luns was not correct" - premeter_err_ct=$premeter_err_ct+1; -fi -if [ $add_host -ne 2 ]; -then - echo "add_host was not correct" - premeter_err_ct=$premeter_err_ct+1; -fi -if [ $num_tgts -ne 2 ]; -then - echo "num_tgts was not correct" - premeter_err_ct=$premeter_err_ct+1; -fi -if [ $premeter_err_ct -eq 0 ]; -then - echo "multiple scsi_debug was loaded as required premeters" -else - echo "multip.e scsi_debug was not loaded as required premeters" -fi -echo "scsi_debug first part of test has been done." - -echo "Now we are doing fs test for scsi_debug driver..." - -cd `dirname $0` -export LTPROOT=${PWD} -echo $LTPROOT | grep testscripts > /dev/null 2>&1 -if [ $? -eq 0 ]; then - cd .. - export LTPROOT=${PWD} -fi - -export TMPBASE="/tmp" - -# check if the newly created scsi_debug partitions are in /proc/partitions file -check_count=$(cat /proc/partitions | wc -l) -save_count=$(( $check_count - $orig_count )) -if [ $save_count -lt 4 ]; then - echo "Not enough scsi_debug partitions to run the test" - exit -fi - -# Get the 4 partitions created by scsi_debug for testing -cat /proc/partitions | awk '{print $4}' | tail -n 4 > $TMPBASE/partition-test -echo "The 4 partitions used to run the test are:" -part1=$(cat $TMPBASE/partition-test | tail -n 1) -echo $part1 -part2=$(cat $TMPBASE/partition-test | head -n 3 | tail -n 1) -echo $part2 -part3=$(cat $TMPBASE/partition-test | head -n 2 | tail -n 1) -echo $part3 -part4=$(cat $TMPBASE/partition-test | head -n 1) -echo $part4 - -export PATH="${PATH}:${LTPROOT}/testcases/bin" - -mkdir /test >/dev/null 2>&1 -mkdir /test/growfiles >/dev/null 2>&1 -mkdir /test/growfiles/ext2 >/dev/null 2>&1 -mkdir /test/growfiles/ext3 >/dev/null 2>&1 -mkdir /test/growfiles/reiser >/dev/null 2>&1 -mkdir /test/growfiles/msdos >/dev/null 2>&1 - -echo "----- make ext3 fs -----" -mkfs -V -t ext3 /dev/$part1 -echo "----- make ext2 fs -----" -mkfs -V -t ext2 /dev/$part2 -echo "----- make reiserfs fs -----" -mkreiserfs -f /dev/$part3 -echo "----- make msdos fs -----" -mkfs -V -t msdos -I /dev/$part4 - -echo "----- Mount partitions -----" -mount /dev/$part1 /test/growfiles/ext3 -mount /dev/$part2 /test/growfiles/ext2 -mount /dev/$part3 /test/growfiles/reiser -mount /dev/$part4 /test/growfiles/msdos - -echo "----- Running tests ----- " -echo "The test may take about 2 hours to finish..." -sort -R ${LTPROOT}/runtest/scsi_debug.part1 -o ${TMPBASE}/scsi_debug - -${LTPROOT}/bin/ltp-pan -e -S -a scsi_debug -n scsi_debug -l ${TMPBASE}/fs-scsi_debug.log -o ${TMPBASE}/fs-scsi_debug.out -f ${TMPBASE}/scsi_debug - -wait $! - -umount -v /dev/$part1 -umount -v /dev/$part2 -umount -v /dev/$part3 -umount -v /dev/$part4 - -echo "Look into /tmp/fs-scsi_debug.log and /tmp/fs-scsi_debug.out for detail results..." diff --git a/testscripts/ltpdmmapper.sh b/testscripts/ltpdmmapper.sh deleted file mode 100755 index f49e5962..00000000 --- a/testscripts/ltpdmmapper.sh +++ /dev/null @@ -1,204 +0,0 @@ -#!/bin/sh -# This script should be run prior to running executing the filesystem tests. -# valid devices need to be passed for Device Mapper to work correctly -# 03/14/03 mridge@us.ibm.com added instance and time command line options - -cd `dirname $0` -export LTPROOT=${PWD} -echo $LTPROOT | grep testscripts > /dev/null 2>&1 -if [ $? -eq 0 ]; then - cd .. - export LTPROOT=${PWD} -fi - -export TMPBASE="/tmp" - - -usage() -{ - cat <<-END >&2 - usage: ${0##*/} [ -a part1 ] [ -b part2 ] - - Note: In order to run this test, you must turn on "device mapper" - component in kernel (it is under device drivers item when you - run make menuconfig); and you must install userspace supporting - files (libdevmapper and dmsetup). They are in the device-mapper - package. You can download it from http://www.sistina.com. Follow - the README/INSTALL file within the package to install it. - - - defaults: - part1=$part1 - part2=$part2 - ltproot=$LTPROOT - tmpdir=$TMPBASE - - example: ${0##*/} -a hdc1 -b hdc2 - - - END -exit -} - -while getopts :a:b: arg -do case $arg in - a) part1=$OPTARG;; - b) part2=$OPTARG;; - - \?) echo "************** Help Info: ********************" - usage;; - esac -done - -if [ ! -n "$part1" ]; then - echo "Missing 1st partition. You must pass 2 partitions for testing" - usage; - exit -fi - -if [ ! -n "$part2" ]; then - echo "Missing 2nd partition. You must pass 2 partitions for testing" - usage; - exit -fi - -echo "Starting Device Mapper Tests..." - -echo "0 10240 linear " $part1 "0" > ltp-dev-mapper-table1 -echo "0 100000 linear " $part1 "0" > ltp-dev-mapper-table2 -echo "0 100000 linear " $part2 "0" > ltp-dev-mapper-table3 -echo "0 200000 striped 2 16 " $part1 "0" $part2 "0" > ltp-dev-mapper-table4 - -echo "Creating Devices..." - -dmsetup create dm-test-1 ltp-dev-mapper-table1 -dmsetup create dm-test-2 ltp-dev-mapper-table2 -dmsetup create dm-test-3 ltp-dev-mapper-table3 -dmsetup create dm-test-4 ltp-dev-mapper-table4 - -echo "Device Info..." - -dmsetup info dm-test-1 -dmsetup info dm-test-2 -dmsetup info dm-test-3 -dmsetup info dm-test-4 - -echo "Device Dependancies..." - -dmsetup deps dm-test-1 -dmsetup deps dm-test-2 -dmsetup deps dm-test-3 -dmsetup deps dm-test-4 - -echo "Device Status..." - -dmsetup status dm-test-1 -dmsetup status dm-test-2 -dmsetup status dm-test-3 -dmsetup status dm-test-4 - -echo "Device Tables..." - -dmsetup table dm-test-1 -dmsetup table dm-test-2 -dmsetup table dm-test-3 -dmsetup table dm-test-4 - -echo "Device Mapper Version..." - -dmsetup version - -echo "Device Waiting..." - -#dmsetup wait dm-test-1 -#dmsetup wait dm-test-2 -#dmsetup wait dm-test-3 -#dmsetup wait dm-test-4 - -echo "Device Mapper Removing Devices..." - -dmsetup remove dm-test-1 -dmsetup remove dm-test-2 -dmsetup remove dm-test-3 -dmsetup remove dm-test-4 - -echo "Device Mapper Re-Creating Devices..." - -dmsetup create dm-test-1 ltp-dev-mapper-table1 -dmsetup create dm-test-2 ltp-dev-mapper-table2 -dmsetup create dm-test-3 ltp-dev-mapper-table3 -dmsetup create dm-test-4 ltp-dev-mapper-table4 - -echo "Re-Naming Devices..." - -dmsetup rename dm-test-1 dm-test-1-new -dmsetup rename dm-test-2 dm-test-2-new -dmsetup rename dm-test-3 dm-test-3-new -dmsetup rename dm-test-4 dm-test-4-new - -echo "Suspend Devices..." - -dmsetup suspend dm-test-1-new -dmsetup suspend dm-test-2-new -dmsetup suspend dm-test-3-new -dmsetup suspend dm-test-4-new - -echo "0 102400 linear " $part1 "0" > ltp-dev-mapper-table1 -echo "0 200000 linear " $part1 "0" > ltp-dev-mapper-table2 -echo "0 200000 linear " $part2 "0" > ltp-dev-mapper-table3 -echo "0 400000 striped 2 16 " $part1 "0" $part2 "0" > ltp-dev-mapper-table4 - -echo "Re-loading Devices..." - -dmsetup reload dm-test-1-new ltp-dev-mapper-table1 -dmsetup reload dm-test-2-new ltp-dev-mapper-table2 -dmsetup reload dm-test-3-new ltp-dev-mapper-table3 -dmsetup reload dm-test-4-new ltp-dev-mapper-table4 - -echo "Resuming Devices..." - -dmsetup resume dm-test-1-new -dmsetup resume dm-test-2-new -dmsetup resume dm-test-3-new -dmsetup resume dm-test-4-new - -echo "Device Info..." - -dmsetup info dm-test-1-new -dmsetup info dm-test-2-new -dmsetup info dm-test-3-new -dmsetup info dm-test-4-new - -echo "Device Dependancies..." - -dmsetup deps dm-test-1-new -dmsetup deps dm-test-2-new -dmsetup deps dm-test-3-new -dmsetup deps dm-test-4-new - -echo "Device Status..." - -dmsetup status dm-test-1-new -dmsetup status dm-test-2-new -dmsetup status dm-test-3-new -dmsetup status dm-test-4-new - -echo "Device Tables..." - -dmsetup table dm-test-1-new -dmsetup table dm-test-2-new -dmsetup table dm-test-3-new -dmsetup table dm-test-4-new - -echo "Device Mapper Remove-all..." - -dmsetup remove_all - -echo "Device Mapper Checking Status - Shouldn't be anything to check" - -dmsetup status dm-test-1-new -dmsetup status dm-test-2-new -dmsetup status dm-test-3-new -dmsetup status dm-test-4-new - - diff --git a/testscripts/sysfs.sh b/testscripts/sysfs.sh deleted file mode 100755 index e22f36fa..00000000 --- a/testscripts/sysfs.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash - - -############################################################## -# -# Copyright (c) International Business Machines Corp., 2003 -# -# 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 -# -# FILE : sysfs.sh -# USAGE : sysfs.sh [ -k ] -# -# DESCRIPTION : A script that will test sysfs on Linux system. -# REQUIREMENTS: CONFIG_DUMMY must have been used to build kernel, and the -# dummy network module must exist. -# -# HISTORY : -# 06/24/2003 Prakash Narayana (prakashn@us.ibm.com) -# -# CODE COVERAGE: 31.3% - fs/sysfs (Total Coverage) -# -# 0.0% - fs/sysfs/bin.c -# 61.8% - fs/sysfs/dir.c -# 27.5% - fs/sysfs/file.c -# 40.4% - fs/sysfs/inode.c -# 41.2% - fs/sysfs/mount.c -# 58.1% - fs/sysfs/symlink.c -# -############################################################## - - -MNT_POINT="/tmp/sysfs_$$" - -KERNEL_NAME=`uname -a | awk ' { print $3 } '` -KERN_MODULE=/lib/modules/$KERNEL_NAME/kernel/drivers/net/dummy.ko -USAGE="$0 [ -k ]" - - -############################################################## -# -# Make sure that uid=root is running this script. -# Validate the command line arguments. -# -############################################################## - -if [ $UID != 0 ] -then - echo "FAILED: Must have root access to execute this script" - exit 1 -fi - -while getopts k: args -do - case $args in - k) KERN_MODULE=$OPTARG ;; - \?) echo $USAGE ; exit 1 ;; - esac -done - -if [ -z "$KERN_MODULE" ] -then - echo $USAGE - echo "FAILED: kernel module to insert not specified" - exit 1 -fi - -# Here is the code coverage for fs/sysfs -# insmod/rmmod net/dummy.ko creates and deletes a directory -# under sysfs. -# In kernel, 2.5.73 or higher, insert/delete base/firmware_class.ko - -mkdir -p -m 777 $MNT_POINT -mount -t sysfs sysfs $MNT_POINT -if [ $? != 0 ] -then - echo "FAILED: sysfs mount failed" - exit 1 -fi - -insmod $KERN_MODULE -if [ $? != 0 ] -then - umount $MNT_POINT - rm -rf $MNT_POINT - echo "FAILED: insmod failed" - exit 1 -fi - -rmmod $KERN_MODULE -if [ $? != 0 ] -then - umount $MNT_POINT - rm -rf $MNT_POINT - echo "FAILED: rmmod failed" - exit 1 -fi - - -####################################################### -# -# Just before exit, perform the cleanup. -# -####################################################### - -umount $MNT_POINT -rm -rf $MNT_POINT - -echo "PASSED: $0 passed!" -exit 0 diff --git a/tools/create-tarballs-metadata.sh b/tools/create-tarballs-metadata.sh index e7f93d5c..227f7e69 100644 --- a/tools/create-tarballs-metadata.sh +++ b/tools/create-tarballs-metadata.sh @@ -1,17 +1,18 @@ -#!/bin/sh +#!/bin/sh -eu +# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2023 Petr Vorel -# Create tarballs and metadata for uploading after tagging release. +# Create tarballs for uploading after tagging release. # https://github.com/linux-test-project/ltp/wiki/LTP-Release-Procedure -set -e + +basedir="$(dirname "$0")" +. "$basedir/lib.sh" tag="$(date +%Y%m%d)" tarball_dir="ltp-full-$tag" extensions="bz2 xz" checksums="md5 sha1 sha256" -git_dir=$(cd $(dirname "$0")/..; pwd) -dir="$(cd $git_dir/../; pwd)/ltp-release-$tag" - -. $(dirname "$0")/lib.sh +git_dir=$(cd "$basedir/.."; pwd) +dir="$(cd "$git_dir/../"; pwd)/ltp-release-$tag" if [ -d $dir ]; then ask "Directory '$dir' exists, will be deleted" @@ -46,11 +47,4 @@ for alg in $checksums; do done done -# metadata documentation -title "Generate metadata documentation" -cd $tarball_dir -rod ./configure --with-metadata-generator=asciidoctor -rod make -C metadata -cp -v docparse/metadata.html $dir/metadata.$tag.html - echo "Generated files are in '$dir', upload them to github" diff --git a/tools/create_dmesg_entries_for_each_test.awk b/tools/create_dmesg_entries_for_each_test.awk index 25d750a5..b21364ae 100755 --- a/tools/create_dmesg_entries_for_each_test.awk +++ b/tools/create_dmesg_entries_for_each_test.awk @@ -27,6 +27,9 @@ NF && ! /^#/ { for (i = 2; i <= NF; i++) { s = s " " $i } + sub(/[;]+$/, "", s) + s = s "; EXIT_CODE=$?" s = s "; dmesg > " DMESG_DIR "/" $1 ".dmesg.log" + s = s "; exit $EXIT_CODE" print s } diff --git a/tools/genhtml.pl b/tools/genhtml.pl index d8f52663..79c178d0 100755 --- a/tools/genhtml.pl +++ b/tools/genhtml.pl @@ -87,6 +87,11 @@ sub get_background_colour_column() { } } +print STDERR "-------------------------------------------------\n"; +print STDERR "INFO: genhtml.pl script is deprecated, try kirk\n"; +print STDERR "(new LTP runner which also generates JSON output)\n"; +print STDERR "https://github.com/linux-test-project/kirk\n"; +print STDERR "-------------------------------------------------\n"; if ($start_tag eq "" || $end_tag eq "" || $output_tag eq "" || $execution_tag eq "") { syntax(); @@ -162,6 +167,7 @@ foreach my $file (@ARGV) { "\n" . "\n"; $row_line =~ s///; + $flag4++; } if ( $flag4 == 3 ) { @variable_value_pair = split(/\ /, $line); @@ -170,7 +176,6 @@ foreach my $file (@ARGV) { $row_line = $row_line . "\n" . "\n"; } - $flag4++; } if ( $line =~ /$execution_tag/ ) { $flag2 = 0; @@ -186,18 +191,23 @@ foreach my $file (@ARGV) { if ($line =~ "termination_id=1" ) { $detected_fail = 1; $failed_test_counter++; + $flag4 = 2; } elsif ($line =~ "termination_id=2" ) { $detected_brok = 1; $brok_test_counter++; + $flag4 = 2; } elsif ($line =~ "termination_id=4" ) { $detected_warn = 1; $warn_test_counter++; + $flag4 = 2; } elsif ($line =~ "termination_id=32" ) { $detected_conf = 1; $conf_test_counter++; + $flag4 = 2; } elsif ($line =~ "termination_id=0" ) { $detected_pass = 1; $test_passed++; + $flag4 = 2; } } if ( $line =~ /$output_tag/ ) { diff --git a/tools/genload/genload.c b/tools/genload/genload.c index 7f56d527..a19d519f 100755 --- a/tools/genload/genload.c +++ b/tools/genload/genload.c @@ -641,9 +641,16 @@ int hogvm(long long forks, long long chunks, long long bytes) /* Use a backoff sleep to ensure we get good fork throughput. */ usleep(backoff); + /* If chunks is 0, ptr will allocate 0 bytes's + * memory, it will cause the process to crash + * during runtime, so adjust to 1 */ + if (chunks == 0) + chunks = 1; + while (1) { - ptr = (char **)malloc(chunks * 2); - for (j = 0; chunks == 0 || j < chunks; j++) { + ptr = (char **)malloc(chunks * + sizeof(char *)); + for (j = 0; j < chunks; j++) { if ((ptr[j] = (char *)malloc(bytes * sizeof(char)))) { @@ -674,10 +681,8 @@ int hogvm(long long forks, long long chunks, long long bytes) if (retval == 0) { dbg(stdout, "hogvm worker freeing memory and starting over\n"); - for (j = 0; chunks == 0 || j < chunks; - j++) { + for (j = 0; j < chunks; j++) free(ptr[j]); - } free(ptr); continue; } diff --git a/tools/genload/stress.c b/tools/genload/stress.c index 7f56d527..a19d519f 100755 --- a/tools/genload/stress.c +++ b/tools/genload/stress.c @@ -641,9 +641,16 @@ int hogvm(long long forks, long long chunks, long long bytes) /* Use a backoff sleep to ensure we get good fork throughput. */ usleep(backoff); + /* If chunks is 0, ptr will allocate 0 bytes's + * memory, it will cause the process to crash + * during runtime, so adjust to 1 */ + if (chunks == 0) + chunks = 1; + while (1) { - ptr = (char **)malloc(chunks * 2); - for (j = 0; chunks == 0 || j < chunks; j++) { + ptr = (char **)malloc(chunks * + sizeof(char *)); + for (j = 0; j < chunks; j++) { if ((ptr[j] = (char *)malloc(bytes * sizeof(char)))) { @@ -674,10 +681,8 @@ int hogvm(long long forks, long long chunks, long long bytes) if (retval == 0) { dbg(stdout, "hogvm worker freeing memory and starting over\n"); - for (j = 0; chunks == 0 || j < chunks; - j++) { + for (j = 0; j < chunks; j++) free(ptr[j]); - } free(ptr); continue; } diff --git a/tools/tag-release.sh b/tools/tag-release.sh index 2967c7b4..b639efb2 100644 --- a/tools/tag-release.sh +++ b/tools/tag-release.sh @@ -1,18 +1,18 @@ -#!/bin/sh +#!/bin/sh -eu +# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2023 Petr Vorel # Tag LTP release. # https://github.com/linux-test-project/ltp/wiki/LTP-Release-Procedure -set -e + +basedir="$(dirname "$0")" +cd "$basedir/.." +. "$basedir/lib.sh" upstream_git="linux-test-project/ltp" tag="$(date +%Y%m%d)" old_tag="$(git describe --abbrev=0)" tag_msg="LTP $tag" -. $(dirname "$0")/lib.sh - -cd $(dirname "$0")/.. - if ! git ls-remote --get-url origin | grep -q $upstream_git; then quit "Not an upstream project" fi diff --git a/utils/benchmark/ebizzy-0.3/README b/utils/benchmark/ebizzy-0.3/README index 6552f420..57797d3b 100755 --- a/utils/benchmark/ebizzy-0.3/README +++ b/utils/benchmark/ebizzy-0.3/README @@ -11,7 +11,7 @@ Compiling --------- First configure ebizzy for your platform by typing "./configure". -Currently Linux and Solaris anre supported. Then type "make". The +Currently Linux and Solaris are supported. Then type "make". The resulting binary will be named "ebizzy". Running diff --git a/utils/sctp/func_tests/test_1_to_1_events.c b/utils/sctp/func_tests/test_1_to_1_events.c index 447845ff..889d2ff3 100755 --- a/utils/sctp/func_tests/test_1_to_1_events.c +++ b/utils/sctp/func_tests/test_1_to_1_events.c @@ -96,6 +96,7 @@ main(void) /* Create the client socket. */ clt_sk = test_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP); + memset(&event, 0, sizeof(struct sctp_event_subscribe)); event.sctp_data_io_event = 1; event.sctp_association_event = 1; event.sctp_shutdown_event = 1; diff --git a/ver_linux b/ver_linux index 7dd0fe17..903fb4e0 100755 --- a/ver_linux +++ b/ver_linux @@ -72,28 +72,40 @@ ld -v 2>&1 | awk -F\) '{print $1}' | awk \ mkswap -V 2>&1 | awk '{print "util-linux ", $NF}' -mount --version 2>&1 | awk -F\- '{print "mount ", $NF}' +mount --version 2>&1 | awk '{$1=$2=""; print "mount ", $0}' insmod -V 2>&1 | awk 'NR==1 {print "modutils ",$NF}' -tune2fs 2>&1 | grep "^tune2fs" | sed 's/,//' | awk \ +bcachefs version 2>&1 | grep "^[0-9]" | awk \ +'NR==1 {print "bcachefs ", $1}' + +mkfs.btrfs -V 2>&1 | grep "^mkfs.btrfs" | sed 's/,//' | awk \ +'NR==1 {print "btrfs ", $5}' + +tune2fs 2>&1 | grep "^tune2fs" | sed 's/,//' | awk \ 'NR==1 {print "e2fsprogs ", $2}' -reiserfsck 2>&1 | grep reiserfsprogs | awk \ -'NR==1{print "reiserfsprogs ", $NF}' +mkfs.exfat -V 2>&1 | grep "^exfatprogs" | sed 's/,//' | awk \ +'NR==1 {print "exfat ", $4}' + +mkfs.ntfs -V 2>&1 | grep "^mkntfs" | sed 's/,//' | awk \ +'NR==1 {$1="";print "ntfs ", $0}' + +mkfs.vfat 2>&1 | grep "^mkfs\." | sed 's/,//' | awk \ +'NR==1 {print "vfat ", $2}' + +mkfs.xfs -V 2>&1 | grep "^mkfs.xfs" | sed 's/,//' | awk \ +'NR==1 {print "xfs ", $3}' cardmgr -V 2>&1| grep version | awk \ 'NR==1{print "pcmcia-cs ", $3}' -pppd --version 2>&1| grep version | awk \ -'NR==1{print "PPP ", $3}' - isdnctrl 2>&1 | grep version | awk \ 'NR==1{print "isdn4k-utils ", $NF}' -ls -l `ldd /bin/sh | awk '/libc/{print $3}'` | sed \ --e 's/\.so$//' | awk -F'[.-]' '{print "Linux C Library " \ -$(NF-2)"."$(NF-1)"."$NF}' +printf "Linux C Library $($(ldd /bin/sh | \ +awk '/libc/{print $3}') 2>&1 | \ +grep -i -e libc.*version -e musl.*libc -e ^version)\n" ldd -v > /dev/null 2>&1 && ldd -v || ldd --version |head -n 1 | awk \ 'NR==1{print "Dynamic linker (ldd) ", $NF}' @@ -125,6 +137,7 @@ loadkeys -V 2>&1 | awk \ expr --v 2>&1 | awk 'NR==1{print "Sh-utils ", $NF}' +echo if [ -e /proc/modules ]; then X=`cat /proc/modules | sed -e "s/ .*$//"` echo "Modules Loaded "$X @@ -158,17 +171,43 @@ else df fi +echo +echo 'tainted (/proc/sys/kernel/tainted):' +cat /proc/sys/kernel/tainted echo +printf 'Secure Boot: ' +if tst_cmd_available mokutil; then + mokutil --sb-state +elif [ -f /sys/firmware/efi/efivars/SecureBoot* ]; then + val=$(od --address-radix=n --format=u1 /sys/firmware/efi/efivars/SecureBoot* | awk 'NR==1 {print $5}') + if [ "$val" = "0" ]; then + echo 'disabled' + elif [ "$val" = "1" ]; then + echo 'enabled' + else + echo 'failed to detect, tying dmesg' + dmesg | grep -i secure.*boot + fi +elif [ ! -f /sys/firmware/efi/efivars/SecureBoot* ]; then + echo 'EFI variables not supported on SUT' +fi +tst_cmd_run 'dmesg | grep -i secure.*boot' + +echo +echo 'Lockdown (/sys/kernel/security/lockdown):' +cat /sys/kernel/security/lockdown + +echo +printf "AppArmor: " if is_enabled /sys/module/apparmor/parameters/enabled; then - echo 'AppArmor enabled' + echo 'enabled' tst_cmd_run aa-status else - echo 'AppArmor disabled' + echo 'disabled' fi echo - if ! tst_cmd_run sestatus; then printf 'SELinux mode: ' tst_cmd_run getenforce || echo 'unknown'
\n"); - i = 0; - } - if (i < 20) - strcpy(tmp_str[i], line2); - - if (strstr(line3, "FSYS=")) { - fprintf(f4, "\n"); - for (k = 0; k < i; k++) { - fprintf(f4, "%s
\n", tmp_str[k]); - } - fprintf(f4, - "
%s %s \n", - line3, line2); - i = 20; - } else if (NULL == strstr(line3, " :")) { - - if (strstr(line3, "(time")) - fprintf(f4, - "
%s
\n", - line3); - else { - k = 0; - p = line3; - while (*p++ != 0) { - if (*p != ' ' && *p != '\n') - k++; - } - if (k > 0) { - fprintf(f4, "%s
\n", line3); - if (i < 20) - i++; - } - } - } - - else if (strstr(line3, "Create")) - fprintf(f4, "
%s  
%6.2f / %6.2f = %.2f  

$termination_id_value[1]

$corefile_value[1]

$cutime_value[1]

$cstime_value[1]