diff options
| author | Martin Matuska <mm@FreeBSD.org> | 2026-08-25 07:55:56 +0000 |
|---|---|---|
| committer | Martin Matuska <mm@FreeBSD.org> | 2026-08-25 07:56:48 +0000 |
| commit | 2c00a4216378d1c7d21cb983d1621a159c755c27 (patch) | |
| tree | 8586065260ea6d4fe6ae5287b30b5b2230019bae | |
| parent | af0e83d9acaa24b51de507d5b01991cf7713783c (diff) | |
| parent | 71a9f9578616a90c3c14bb59629fb4d31bfd68d1 (diff) | |
zfs: merge openzfs/zfs@71a9f9578
OpenZFS 2.4.4
Notable upstream pull request merges:
#18544 eb4199c6a zio_ddt_write: compute have_dvas after taking dde_io_lock
#18573 0da2e6b39 Update mtime/ctime when fallocate grows a file
#18611 b021ebcdc zfs_ioctl: fix EBUSY race between quota queries and mount
#18652 027940e0b zbookmark_compare: handle "marker" bookmarks with
negative levels
#18657 14ff1853c Fix race between device removal completion and pool export
#18673 -multiple delegate: add send:encrypted permission
#18688 5f84f4d81 Fix handling of _PC_HAS_HIDDENSYSTEM for FreeBSD
#18693 7e8f6ad78 Clean up embedded slog metaslab across txgs
#18706 5e8780ff9 ddt_log: Fix refcount tagging for begin/commit
#18713 a2c02072f libzfs: fix MS_CRYPT/MS_OVERLAY collision with umount2(2)
flags
#18720 09a876a38 Fix insufficient locking in dedup verify
#18724 a6f557c2c Fix reads for blocks freed after being cloned
#18738 b4411799b Fix receive -x according to comment
#18749 47f32602d Fix receive of split large blocks with a short trailing
chunk
#18795 d6bbb80ae Rate limit Direct I/O verify zevents
#18802 f5330ea34 libzfs: fallback VDEV_UPATH to VDEV_PATH for non-DMxi
devices
#18822 292c190b8 libspl: Implement VERIFY_IMPLY and VERIFY_EQUIV
#18827 36a398de2 L2ARC: bound the rebuild by the write hand on a first
sweep
#18833 a052ffc64 zed: let autoexpand see capacity changes on partitioned
disks
#18835 9623afdb9 mmp: skip non-writeable vdevs during activity check
#18838 fee680504 DDT: Fix several bugs in pruning
#18840 eb808b3f4 arc: add a few invariant checks in release builds
#18841 cc010bbc3 zpool export: return EBUSY when zvol minors are in use
#18848 5f9bea114 dmu_recv: Avoid potential null deref
#18855 9683c86c4 mmp: do not require writes to mirror legs the config
marks absent
#18865 51421b0ef Fix DMU bonus hold leak on I/O error
#18867 49086325b Add missing checks to zfs_clone_range_replay()
#18868 223361144 libzfs: String trimming should not operate out of bounds
#18869 17834383e libzfs: Do not call munmap() when mmap() fails
#18871 bcf2999db libzfs: don't truncate a resolved vdev path in
zpool_vdev_name()
#18874 02861fdeb nvpair: Fix operator precedence
#18876 ec565e143 nvpair: Improve native handling of unterminated strings
#18877 88c5af639 nvpair: i_get_value_size() string array handling tweak
#18883 8be87df61 libzfs: don't read a dataset handle after closing it in
resume send
#18886 c714cc5a3 Fix negative time overflows in DDT pruning
#18892 -multiple zhack: add "mmp reclaim" to recover a pool stranded by
MMP
#18899 9926f72b8 CodeQL: Flag implicit compare-then-assign in branch
conditions
Obtained from: OpenZFS
OpenZFS commit: 71a9f9578616a90c3c14bb59629fb4d31bfd68d1
OpenZFS tag: zfs-2.4.4
167 files changed, 5103 insertions, 1202 deletions
diff --git a/sys/contrib/openzfs/.github/PULL_REQUEST_TEMPLATE.md b/sys/contrib/openzfs/.github/PULL_REQUEST_TEMPLATE.md index 47edc8174603..3a69b558b6c2 100644 --- a/sys/contrib/openzfs/.github/PULL_REQUEST_TEMPLATE.md +++ b/sys/contrib/openzfs/.github/PULL_REQUEST_TEMPLATE.md @@ -16,7 +16,7 @@ <!--- If your change is a performance enhancement, please provide benchmarks here. --> <!--- Please think about using the draft PR feature if appropriate --> -### Types of changes +### Types of Changes <!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) @@ -24,10 +24,10 @@ - [ ] Code cleanup (non-breaking change which makes code smaller or more readable) - [ ] Quality assurance (non-breaking change which makes the code more robust against bugs) - [ ] Breaking change (fix or feature that would cause existing functionality to change) -- [ ] Library ABI change (libzfs, libzfs\_core, libnvpair, libuutil and libzfsbootenv) +- [ ] Library ABI change (libzfs, libzfs\_core, libnvpair and libzfsbootenv) - [ ] Documentation (a change to man pages or other documentation) -### Checklist: +### Checklist <!--- Go over all the following points, and put an `x` in all the boxes that apply. --> <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - [ ] My code follows the OpenZFS [code style requirements](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#coding-conventions). diff --git a/sys/contrib/openzfs/.github/codeql-cpp.yml b/sys/contrib/openzfs/.github/codeql-cpp.yml index d99cdb559244..15a5a68ae67b 100644 --- a/sys/contrib/openzfs/.github/codeql-cpp.yml +++ b/sys/contrib/openzfs/.github/codeql-cpp.yml @@ -3,3 +3,4 @@ name: "Custom CodeQL Analysis" queries: - uses: ./.github/codeql/custom-queries/cpp/deprecatedFunctionUsage.ql - uses: ./.github/codeql/custom-queries/cpp/dslDatasetHoldReleMismatch.ql + - uses: ./.github/codeql/custom-queries/cpp/AssignmentOfComparisonAsCondition.ql diff --git a/sys/contrib/openzfs/.github/codeql/custom-queries/cpp/AssignmentOfComparisonAsCondition.ql b/sys/contrib/openzfs/.github/codeql/custom-queries/cpp/AssignmentOfComparisonAsCondition.ql new file mode 100644 index 000000000000..394c16eb1146 --- /dev/null +++ b/sys/contrib/openzfs/.github/codeql/custom-queries/cpp/AssignmentOfComparisonAsCondition.ql @@ -0,0 +1,96 @@ +/** + * @name Ambiguous assignment-of-comparison in a condition + * @description Finds any assignment (`=`, `+=`, `-=`, `|=`, …) that appears in + * a branching condition and whose rvalue is an unparenthesized + * comparison. + * + * Comparison binds tighter than assignment, so + * `if ((x = foo() < 0))` / `if ((x += foo() < 0))` + * assigns the boolean result of the comparison. Programmers often + * meant + * `if ((x = foo()) < 0)` / `if ((x += foo()) < 0)` + * (assign/update then compare). GCC `-Wparentheses` is silenced by + * the outer parentheses in both forms, so the compiler does not + * catch the mistake (see openzfs/zfs#18874). + * + * Rule (intentionally simple): + * assignment in a branch condition + * AND rvalue is a comparison + * AND that comparison is not parenthesized. + * + * That is enough: + * - Explicit assign-then-compare `((x = foo()) < 0)` has no + * comparison as the assignment's rvalue. + * - Explicit compare-then-assign `((x = (foo() < 0)))` has a + * parenthesized comparison as the rvalue. + * - Parentheses that only wrap a subexpression of the comparison + * (e.g. `(foo())`) do not count as parenthesizing the + * comparison itself. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/ambiguous-assignment-of-comparison-in-condition + * @tags reliability + * correctness + * external/cwe/cwe-783 + * external/cwe/cwe-480 + */ + +import cpp + +/** + * The condition expression of a branching construct + * (if / while / do-while / for / ternary `?:`). + */ +Expr branchCondition() { + result = any(IfStmt s).getCondition() + or + result = any(WhileStmt s).getCondition() + or + result = any(DoStmt s).getCondition() + or + result = any(ForStmt s).getCondition() + or + result = any(ConditionalExpr c).getCondition() +} + +/** + * Holds if `e` is the branch condition, or appears anywhere under it + * (including under `&&` / `||` / `!`, and through parentheses/conversions). + */ +predicate inBranchCondition(Expr e) { + e = branchCondition() + or + // Structural child of something already in a branch condition + exists(Expr parent | + parent.getAChild() = e and + inBranchCondition(parent) + ) + or + // ParenthesisExpr and other conversions hang off getConversion(), not getAChild() + exists(Expr base | + base.getConversion+() = e and + inBranchCondition(base) + ) + or + exists(Expr conv | + e.getConversion+() = conv and + inBranchCondition(conv) + ) +} + +from Assignment a, ComparisonOperation cmp +where + // Core rule: assignment in a branch condition whose rvalue is an + // unparenthesized comparison. Covers `=` and all compound forms (`+=`, …) + // because Assignment is the common base class. + a.getRValue() = cmp and + not cmp.isParenthesised() and + inBranchCondition(a) and + // Skip AST from templates that were never instantiated (incomplete / non-code). + not a.isFromUninstantiatedTemplate(_) +select a, + "Assignment (`" + a.getOperator() + + "`) in a branch condition has an unparenthesized comparison as its rvalue; " + + "likely meant `((x " + a.getOperator() + " expr) op val)` rather than " + + "`(x " + a.getOperator() + " expr op val)`." diff --git a/sys/contrib/openzfs/.github/workflows/README.md b/sys/contrib/openzfs/.github/workflows/README.md index 78774aac52f9..434fc92b573c 100644 --- a/sys/contrib/openzfs/.github/workflows/README.md +++ b/sys/contrib/openzfs/.github/workflows/README.md @@ -75,7 +75,7 @@ Auto-selected: - Linux: almalinux 8/9/10, centos-stream 9/10, debian 11/12/13, fedora 43/44, ubuntu 22/24/26 -- FreeBSD: 14.4-RELEASE/STABLE, 15.0-RELEASE, 15.1-STABLE, 16.0-CURRENT +- FreeBSD: 14.4-RELEASE/STABLE, 15.1-RELEASE/STABLE, 16.0-CURRENT Available via `specific_os` or `ZTS_OS_OVERRIDE`: diff --git a/sys/contrib/openzfs/.github/workflows/scripts/qemu-2-start.sh b/sys/contrib/openzfs/.github/workflows/scripts/qemu-2-start.sh index 7e72030adb99..80dd60859416 100755 --- a/sys/contrib/openzfs/.github/workflows/scripts/qemu-2-start.sh +++ b/sys/contrib/openzfs/.github/workflows/scripts/qemu-2-start.sh @@ -44,11 +44,11 @@ case "$OS" in OSv="almalinux9" URL="https://repo.almalinux.org/almalinux/10/cloud/x86_64/images/AlmaLinux-10-GenericCloud-latest.x86_64.qcow2" ;; - alpine3-23) - OSNAME="Alpine Linux 3.23.2" + alpine3-24) + OSNAME="Alpine Linux 3.24.1" # Alpine Linux v3.22 and v3.23 are unknown to osinfo as of 2025-12-26. OSv="alpinelinux3.21" - URL="https://dl-cdn.alpinelinux.org/alpine/v3.23/releases/cloud/generic_alpine-3.23.2-x86_64-bios-cloudinit-r0.qcow2" + URL="https://dl-cdn.alpinelinux.org/alpine/v3.24/releases/cloud/generic_alpine-3.24.1-x86_64-bios-cloudinit-r0.qcow2" ;; archlinux) OSNAME="Archlinux" @@ -107,8 +107,8 @@ case "$OS" in URLxz="$FREEBSD_REL/$FreeBSD/amd64/Latest/FreeBSD-$FreeBSD-amd64-BASIC-CI.raw.xz" KSRC="$FREEBSD_REL/../amd64/$FreeBSD/src.txz" ;; - freebsd15-0r) - FreeBSD="15.0-RELEASE" + freebsd15-1r) + FreeBSD="15.1-RELEASE" OSNAME="FreeBSD $FreeBSD" OSv="freebsd14.0" URLxz="$FREEBSD_REL/$FreeBSD/amd64/Latest/FreeBSD-$FreeBSD-amd64-BASIC-CI-ufs.raw.xz" @@ -349,7 +349,7 @@ else while pidof /usr/bin/qemu-system-x86_64 >/dev/null; do ssh 2>/dev/null root@vm0 "uname -a" && break done - ssh root@vm0 "env IGNORE_OSVERSION=yes pkg install -y bash ca_root_nss git qemu-guest-agent python3 py311-cloud-init" + ssh root@vm0 "env IGNORE_OSVERSION=yes pkg install -y bash ca_root_nss git qemu-guest-agent python3 net/cloud-init" ssh root@vm0 "chsh -s $BASH root" ssh root@vm0 'sysrc qemu_guest_agent_enable="YES"' ssh root@vm0 'sysrc cloudinit_enable="YES"' diff --git a/sys/contrib/openzfs/.github/workflows/scripts/qemu-3-deps-vm.sh b/sys/contrib/openzfs/.github/workflows/scripts/qemu-3-deps-vm.sh index d61e97cf423e..06e273615681 100755 --- a/sys/contrib/openzfs/.github/workflows/scripts/qemu-3-deps-vm.sh +++ b/sys/contrib/openzfs/.github/workflows/scripts/qemu-3-deps-vm.sh @@ -16,7 +16,7 @@ set -eu function alpine() { echo "##[group]Install Development Tools" sudo apk add \ - acl alpine-sdk attr autoconf automake bash build-base clang21 coreutils \ + acl alpine-sdk attr autoconf automake bash build-base clang22 coreutils \ cpio cryptsetup curl curl-dev dhcpcd eudev eudev-dev eudev-libs findutils \ fio gawk gdb gettext-dev git grep jq libaio libaio-dev libcurl \ libtirpc-dev libtool libunwind libunwind-dev linux-headers linux-tools \ diff --git a/sys/contrib/openzfs/.github/workflows/scripts/qemu-4-build-vm.sh b/sys/contrib/openzfs/.github/workflows/scripts/qemu-4-build-vm.sh index dfe70c4f1ef4..879e98efd5a4 100755 --- a/sys/contrib/openzfs/.github/workflows/scripts/qemu-4-build-vm.sh +++ b/sys/contrib/openzfs/.github/workflows/scripts/qemu-4-build-vm.sh @@ -400,13 +400,12 @@ case "$OS" in ;; alma*|centos*) rpm_build_and_install "--with-spec=redhat $extra" + if [ -n "$TARBALL" ] ; then + build_tarball + fi ;; fedora*) rpm_build_and_install "$extra" - - # Historically, we've always built the release tarballs on Fedora, since - # there was one instance long ago where we built them on CentOS 7, and they - # didn't work correctly for everyone. if [ -n "$TARBALL" ] ; then build_tarball fi diff --git a/sys/contrib/openzfs/.github/workflows/scripts/qemu-6-tests.sh b/sys/contrib/openzfs/.github/workflows/scripts/qemu-6-tests.sh index a0612e5e0b2b..94f777e839c2 100755 --- a/sys/contrib/openzfs/.github/workflows/scripts/qemu-6-tests.sh +++ b/sys/contrib/openzfs/.github/workflows/scripts/qemu-6-tests.sh @@ -23,8 +23,14 @@ function prefix() { M=$((DIFF/60)) S=$((DIFF-(M*60))) - CTR=$(cat /tmp/ctr) - echo $LINE| grep -q '^\[.*] Test[: ]' && CTR=$((CTR+1)) && echo $CTR > /tmp/ctr + CTR=$(cat /tmp/ctr-vm${ID}) + # Publish the counter with a rename so it is never momentarily empty: the + # other VM's reader reads this file, and a plain redirect truncates before + # it writes. A read landing in that window fails, which under set -e ends + # the reader and silently stops prefixing that VM's output. + echo $LINE| grep -q '^\[.*] Test[: ]' && CTR=$((CTR+1)) \ + && echo $CTR > "/tmp/ctr-vm${ID}.new" \ + && mv "/tmp/ctr-vm${ID}.new" "/tmp/ctr-vm${ID}" BASE="$HOME/work/zfs/zfs" COLOR="$BASE/scripts/zfs-tests-color.sh" @@ -35,9 +41,17 @@ function prefix() { if [ -z "$CLINE" ]; then printf "vm${ID}: %s\n" "$LINE" else - # [vm2: 00:15:54 256] Test: functional/checksum/setup (run as root) [00:00] [PASS] - printf "[vm${ID}: %02d:%02d:%02d %4d] %s\n" \ - "$H" "$M" "$S" "$CTR" "$CLINE" + TOTAL=0 + BREAKDOWN="" + TOTAL_VMS="$3" + for ((v=1; v<=TOTAL_VMS; v++)); do + read -r val < "/tmp/ctr-vm${v}" + TOTAL=$((TOTAL + val)) + BREAKDOWN="${BREAKDOWN}${BREAKDOWN:+|}${val}" + done + # [vm2: 00:15:54 256(200|56)] Test: functional/checksum/setup (run as root) [00:00] [PASS] + printf "[vm${ID}: %02d:%02d:%02d %4d(%s)] %s\n" \ + "$H" "$M" "$S" "$TOTAL" "$BREAKDOWN" "$CLINE" fi } @@ -96,10 +110,10 @@ if [ -z ${1:-} ]; then source env.txt SSH=$(which ssh) TESTS='$HOME/zfs/.github/workflows/scripts/qemu-6-tests.sh' - echo 0 > /tmp/ctr date "+%s" > /tmp/tsstart for ((i=1; i<=VMs; i++)); do + echo 0 > /tmp/ctr-vm${i} IP="192.168.122.1$i" # We do an additional test build of Lustre against ZFS if we're vm2 @@ -121,7 +135,7 @@ if [ -z ${1:-} ]; then $SSH zfs@$IP $TESTS $OS $i $VMs $extra $CI_TYPE # handly line by line and add info prefix stdbuf -oL tail -fq vm${i}log.txt \ - | while read -r line; do prefix "$i" "$line"; done & + | while read -r line; do prefix "$i" "$line" "$VMs"; done & echo $! > vm${i}log.pid # don't mix up the initial --- Configuration --- part sleep 0.13 @@ -132,7 +146,10 @@ if [ -z ${1:-} ]; then tail --pid=$(cat vm${i}.pid) -f /dev/null pid=$(cat vm${i}log.pid) rm -f vm${i}log.pid - kill $pid + # Tolerate a reader that has already exited. The ESRCH would otherwise + # end this script under set -e and fail a job whose tests all passed. + # It stays on stderr: a dead reader means output was lost. + kill $pid || true done exit 0 @@ -187,12 +204,16 @@ case "$OS" in sudo chmod 1777 /var/tmp sudo mv -f /tmp/*.txt /var/tmp - # Allow for longer RCU timeouts due to the heavily virtualized and - # potentially oversubscribed nature of the CI environment. + # Allow for longer RCU and watchdog timeouts due to the heavily virtualized + # and potentially over-subscribed nature of the CI environment. rcu_cpu_stall_timeout="/sys/module/rcupdate/parameters/rcu_cpu_stall_timeout" if test -f $rcu_cpu_stall_timeout; then echo 120 | sudo sh -c "cat > '$rcu_cpu_stall_timeout'" fi + + if test -c /dev/watchdog; then + sudo wdctl --settimeout 120 >/dev/null + fi ;; esac diff --git a/sys/contrib/openzfs/.github/workflows/zfs-qemu.yml b/sys/contrib/openzfs/.github/workflows/zfs-qemu.yml index 4b9e16254622..8aca48091a72 100644 --- a/sys/contrib/openzfs/.github/workflows/zfs-qemu.yml +++ b/sys/contrib/openzfs/.github/workflows/zfs-qemu.yml @@ -14,7 +14,7 @@ on: type: string required: false default: "" - description: "(optional) Only run on this specific OS (like 'fedora44' or 'alpine3-23')" + description: "(optional) Only run on this specific OS (like 'fedora44' or 'alpine3-24')" concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -23,7 +23,7 @@ concurrency: jobs: test-config: name: Setup - if: github.event_name == 'pull_request' || github.repository != 'openzfs/zfs' + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.repository != 'openzfs/zfs' runs-on: ubuntu-24.04 outputs: test_os: ${{ steps.os.outputs.os }} @@ -53,11 +53,11 @@ jobs: os_selection='["almalinux8", "almalinux9", "almalinux10", "centos-stream9", "centos-stream10", "debian11", "debian12", "debian13", "fedora43", "fedora44", "ubuntu22", "ubuntu24", "ubuntu26"]' ;; freebsd) - os_selection='["freebsd14-4r", "freebsd14-4s", "freebsd15-0r", "freebsd15-1s", "freebsd16-0c"]' + os_selection='["freebsd14-4r", "freebsd14-4s", "freebsd15-1r", "freebsd15-1s", "freebsd16-0c"]' ;; *) # default list - os_selection='["almalinux8", "almalinux9", "almalinux10", "centos-stream9", "centos-stream10", "debian12", "debian13", "fedora43", "fedora44", "freebsd14-4r", "freebsd15-0r", "freebsd15-1s", "freebsd16-0c", "ubuntu22", "ubuntu24", "ubuntu26"]' + os_selection='["almalinux8", "almalinux9", "almalinux10", "centos-stream9", "centos-stream10", "debian12", "debian13", "fedora43", "fedora44", "freebsd14-4r", "freebsd15-1r", "freebsd15-1s", "freebsd16-0c", "ubuntu22", "ubuntu24", "ubuntu26"]' ;; esac @@ -103,7 +103,7 @@ jobs: # debian: debian12, debian13, ubuntu22, ubuntu24, ubuntu26 # misc: archlinux, tumbleweed # FreeBSD variants of november 2025: - # FreeBSD Release: freebsd14-4r, freebsd15-0r + # FreeBSD Release: freebsd14-4r, freebsd15-1r # FreeBSD Stable: freebsd14-4s, freebsd15-1s # FreeBSD Current: freebsd16-0c os: ${{ fromJson(needs.test-config.outputs.test_os) }} diff --git a/sys/contrib/openzfs/.gitignore b/sys/contrib/openzfs/.gitignore index a2cb92dd5406..2ebf07c86e0b 100644 --- a/sys/contrib/openzfs/.gitignore +++ b/sys/contrib/openzfs/.gitignore @@ -59,6 +59,7 @@ !NOTICE !README.md !RELEASES.md +!SECURITY.md !TEST !zfs.release.in diff --git a/sys/contrib/openzfs/META b/sys/contrib/openzfs/META index cd8936d389e7..8133bf1e36f9 100644 --- a/sys/contrib/openzfs/META +++ b/sys/contrib/openzfs/META @@ -1,10 +1,10 @@ Meta: 1 Name: zfs Branch: 1.0 -Version: 2.4.3 +Version: 2.4.4 Release: 1 Release-Tags: relext License: CDDL Author: OpenZFS -Linux-Maximum: 7.0 +Linux-Maximum: 7.2 Linux-Minimum: 4.18 diff --git a/sys/contrib/openzfs/README.md b/sys/contrib/openzfs/README.md index 7aa09cc324f2..aa91881255a0 100644 --- a/sys/contrib/openzfs/README.md +++ b/sys/contrib/openzfs/README.md @@ -68,4 +68,4 @@ Generally, if a distribution is following an LTS kernel, it should work well wit All FreeBSD releases receiving **security support** are supported by OpenZFS. -**Supported FreeBSD releases**: **15.0**, **14.4**. +**Supported FreeBSD releases**: **15.1**, **14.4**. diff --git a/sys/contrib/openzfs/SECURITY.md b/sys/contrib/openzfs/SECURITY.md new file mode 100644 index 000000000000..5d69e8d8b71e --- /dev/null +++ b/sys/contrib/openzfs/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Reporting a Vulnerability + +We encourage responsible disclosure of security vulnerabilities. If you +find something suspicious, we encourage and appreciate your report. + +The preferred way to report a vulnerability is to use the +**"Report a vulnerability"** button under the **Security** tab of the +OpenZFS GitHub repository. This creates a private communication channel +between you and the maintainers, allowing us to review the report +confidentially and respond as quickly as possible. + +Please include, if possible: + +- A clear description of the issue +- Steps to reproduce the problem +- Affected versions or branches +- Any proof of concept, logs, or screenshots +- Your assessment of the potential impact + +## What to Expect + +- We will review security reports as soon as practical. +- We may ask follow-up questions to better understand the issue. +- Please allow time for investigation and coordination before public + disclosure. +- If the issue is confirmed, we will work on a fix and release a security + update as needed for supported OpenZFS versions. diff --git a/sys/contrib/openzfs/cmd/zdb/zdb.c b/sys/contrib/openzfs/cmd/zdb/zdb.c index 62d8cba878a9..acabafc5208c 100644 --- a/sys/contrib/openzfs/cmd/zdb/zdb.c +++ b/sys/contrib/openzfs/cmd/zdb/zdb.c @@ -36,6 +36,7 @@ * Copyright (c) 2021 Toomas Soome <tsoome@me.com> * Copyright (c) 2023, 2024, Klara Inc. * Copyright (c) 2023, Rob Norris <robn@despairlabs.com> + * Copyright 2026 Edgecast Cloud LLC. */ #include <stdio.h> @@ -1436,10 +1437,10 @@ dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size) zap_attribute_free(attrp); } -static int +static uint64_t get_dtl_refcount(vdev_t *vd) { - int refcount = 0; + uint64_t refcount = 0; if (vd->vdev_ops->vdev_op_leaf) { space_map_t *sm = vd->vdev_dtl_sm; @@ -1455,10 +1456,10 @@ get_dtl_refcount(vdev_t *vd) return (refcount); } -static int +static uint64_t get_metaslab_refcount(vdev_t *vd) { - int refcount = 0; + uint64_t refcount = 0; if (vd->vdev_top == vd) { for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { @@ -1475,11 +1476,11 @@ get_metaslab_refcount(vdev_t *vd) return (refcount); } -static int +static uint64_t get_obsolete_refcount(vdev_t *vd) { uint64_t obsolete_sm_object; - int refcount = 0; + uint64_t refcount = 0; VERIFY0(vdev_obsolete_sm_object(vd, &obsolete_sm_object)); if (vd->vdev_top == vd && obsolete_sm_object != 0) { @@ -1500,7 +1501,7 @@ get_obsolete_refcount(vdev_t *vd) return (refcount); } -static int +static uint64_t get_prev_obsolete_spacemap_refcount(spa_t *spa) { uint64_t prev_obj = @@ -1515,10 +1516,10 @@ get_prev_obsolete_spacemap_refcount(spa_t *spa) return (0); } -static int +static uint64_t get_checkpoint_refcount(vdev_t *vd) { - int refcount = 0; + uint64_t refcount = 0; if (vd->vdev_top == vd && vd->vdev_top_zap != 0 && zap_contains(spa_meta_objset(vd->vdev_spa), @@ -1531,7 +1532,7 @@ get_checkpoint_refcount(vdev_t *vd) return (refcount); } -static int +static uint64_t get_log_spacemap_refcount(spa_t *spa) { return (avl_numnodes(&spa->spa_sm_logs_by_txg)); @@ -1541,23 +1542,37 @@ static int verify_spacemap_refcounts(spa_t *spa) { uint64_t expected_refcount = 0; - uint64_t actual_refcount; + uint64_t actual_refcount = 0; + uint64_t dtl_refcount, metaslab_refcount, obsolete_refcount, + prev_obsolete_refcount, checkpoint_refcount, log_spacemap_refcount; (void) feature_get_refcount(spa, &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM], &expected_refcount); - actual_refcount = get_dtl_refcount(spa->spa_root_vdev); - actual_refcount += get_metaslab_refcount(spa->spa_root_vdev); - actual_refcount += get_obsolete_refcount(spa->spa_root_vdev); - actual_refcount += get_prev_obsolete_spacemap_refcount(spa); - actual_refcount += get_checkpoint_refcount(spa->spa_root_vdev); - actual_refcount += get_log_spacemap_refcount(spa); + dtl_refcount = get_dtl_refcount(spa->spa_root_vdev); + metaslab_refcount = get_metaslab_refcount(spa->spa_root_vdev); + obsolete_refcount = get_obsolete_refcount(spa->spa_root_vdev); + prev_obsolete_refcount = get_prev_obsolete_spacemap_refcount(spa); + checkpoint_refcount = get_checkpoint_refcount(spa->spa_root_vdev); + log_spacemap_refcount = get_log_spacemap_refcount(spa); + actual_refcount = dtl_refcount + metaslab_refcount + + obsolete_refcount + prev_obsolete_refcount + + checkpoint_refcount + log_spacemap_refcount; if (expected_refcount != actual_refcount) { - (void) printf("space map refcount mismatch: expected %lld != " - "actual %lld\n", - (longlong_t)expected_refcount, - (longlong_t)actual_refcount); + (void) printf("space map refcount mismatch: expected %llu != " + "actual %llu\n", + (u_longlong_t)expected_refcount, + (u_longlong_t)actual_refcount); + (void) printf("\tDTL: %llu, metaslab: %llu, obsolete: %llu, " + "prev obsolete: %llu, checkpoint: %llu, " + "log spacemap: %llu\n", + (u_longlong_t)dtl_refcount, + (u_longlong_t)metaslab_refcount, + (u_longlong_t)obsolete_refcount, + (u_longlong_t)prev_obsolete_refcount, + (u_longlong_t)checkpoint_refcount, + (u_longlong_t)log_spacemap_refcount); return (2); } return (0); diff --git a/sys/contrib/openzfs/cmd/zed/agents/zfs_mod.c b/sys/contrib/openzfs/cmd/zed/agents/zfs_mod.c index 57d6f34ba74a..a055fe524b9f 100644 --- a/sys/contrib/openzfs/cmd/zed/agents/zfs_mod.c +++ b/sys/contrib/openzfs/cmd/zed/agents/zfs_mod.c @@ -1080,8 +1080,63 @@ zfsdle_vdev_online(zpool_handle_t *zhp, void *data) zed_log_msg(LOG_INFO, "zfsdle_vdev_online: searching for '%s' in '%s'", devname, zpool_get_name(zhp)); - if ((tgt = zpool_find_vdev_by_physpath(zhp, devname, - &avail_spare, &l2cache, NULL)) != NULL) { + tgt = zpool_find_vdev_by_physpath(zhp, devname, + &avail_spare, &l2cache, NULL); + + /* + * A whole-disk event carries no vdev guid (the label lives on + * the partition), and udev provides no ID_PATH on some buses, + * so neither lookup above can match. Identify the vdev by + * reading the label off the whole-disk partition instead: the + * pool and vdev guids stored there don't depend on which name + * the pool was imported with (by-id, by-path or a bare device + * node), and they can't be forged by a stale config path left + * behind in an unrelated pool. + */ + if (tgt == NULL && nvlist_lookup_uint64(udev_nvl, ZFS_EV_VDEV_GUID, + &guid) != 0 && nvlist_lookup_string(udev_nvl, DEV_NAME, + &tmp_devname) == 0 && !nvlist_exists(udev_nvl, DEV_IS_PART)) { + nvlist_t *label = NULL; + uint64_t label_pool_guid = 0, label_vdev_guid = 0; + int fd; + + strlcpy(devname, tmp_devname, MAXPATHLEN); + zfs_append_partition(devname, MAXPATHLEN); + + if ((fd = open(devname, O_RDONLY | O_CLOEXEC)) >= 0) { + if (zpool_read_label(fd, &label, NULL) == 0 && + label != NULL && + nvlist_lookup_uint64(label, + ZPOOL_CONFIG_POOL_GUID, &label_pool_guid) == 0 && + nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, + &label_vdev_guid) == 0 && + label_pool_guid == zpool_get_prop_int(zhp, + ZPOOL_PROP_GUID, NULL)) { + zed_log_msg(LOG_INFO, "zfsdle_vdev_online: " + "matched vdev %llu by the label on '%s'", + (u_longlong_t)label_vdev_guid, devname); + + (void) snprintf(devname, MAXPATHLEN, "%llu", + (u_longlong_t)label_vdev_guid); + tgt = zpool_find_vdev(zhp, devname, + &avail_spare, &l2cache, NULL); + if (tgt != NULL && (avail_spare || l2cache)) + tgt = NULL; + if (tgt != NULL) { + uint64_t wd = 0; + + (void) nvlist_lookup_uint64(tgt, + ZPOOL_CONFIG_WHOLE_DISK, &wd); + if (!wd) + tgt = NULL; + } + } + nvlist_free(label); + (void) close(fd); + } + } + + if (tgt != NULL) { const char *path; char fullpath[MAXPATHLEN]; uint64_t wholedisk = 0; diff --git a/sys/contrib/openzfs/cmd/zed/zed_disk_event.c b/sys/contrib/openzfs/cmd/zed/zed_disk_event.c index 8b0a1c499449..d522d7a0e298 100644 --- a/sys/contrib/openzfs/cmd/zed/zed_disk_event.c +++ b/sys/contrib/openzfs/cmd/zed/zed_disk_event.c @@ -242,6 +242,9 @@ zed_udev_monitor(void *arg) part != NULL && part[0] != '\0') { const char *devname = udev_device_get_property_value(dev, "DEVNAME"); + const char *resize = + udev_device_get_property_value(dev, "RESIZE"); + const char *dev_action = udev_device_get_action(dev); if (strcmp(part, "atari") == 0) { zed_log_msg(LOG_INFO, @@ -249,6 +252,19 @@ zed_udev_monitor(void *arg) "but we're going to assume it's a false " "positive and still use it (issue #13497)", __func__, devname); + } else if (dev_action != NULL && + strcmp(dev_action, "change") == 0 && + resize != NULL && strcmp(resize, "1") == 0) { + /* + * A capacity change is reported on the disk + * itself, not on its partitions, so this is + * the only chance to notice it. Pass the + * event on for a possible autoexpand of a + * whole-disk vdev (issue #12505). + */ + zed_log_msg(LOG_INFO, + "%s: %s capacity change on partitioned " + "disk", __func__, devname); } else { zed_log_msg(LOG_INFO, "%s: skip %s since it has a %s partition " diff --git a/sys/contrib/openzfs/cmd/zfs/zfs_main.c b/sys/contrib/openzfs/cmd/zfs/zfs_main.c index ce948951a028..9b58286272a6 100644 --- a/sys/contrib/openzfs/cmd/zfs/zfs_main.c +++ b/sys/contrib/openzfs/cmd/zfs/zfs_main.c @@ -32,6 +32,7 @@ * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com> * Copyright 2019 Joyent, Inc. * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved. + * Copyright (c) 2026, TrueNAS. */ #include <assert.h> @@ -5332,6 +5333,7 @@ zfs_do_receive(int argc, char **argv) #define ZFS_DELEG_PERM_SHARE "share" #define ZFS_DELEG_PERM_SEND "send" #define ZFS_DELEG_PERM_SEND_RAW "send:raw" +#define ZFS_DELEG_PERM_SEND_ENCRYPTED "send:encrypted" #define ZFS_DELEG_PERM_RECEIVE "receive" #define ZFS_DELEG_PERM_RECEIVE_APPEND "receive:append" #define ZFS_DELEG_PERM_ALLOW "allow" @@ -5375,6 +5377,7 @@ static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK }, { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND }, { ZFS_DELEG_PERM_SEND_RAW, ZFS_DELEG_NOTE_SEND_RAW }, + { ZFS_DELEG_PERM_SEND_ENCRYPTED, ZFS_DELEG_NOTE_SEND_ENCRYPTED }, { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK }, @@ -5957,11 +5960,15 @@ deleg_perm_comment(zfs_deleg_note_t note) str = gettext(""); break; case ZFS_DELEG_NOTE_SEND: - str = gettext(""); + str = gettext("Allow sending datasets"); break; case ZFS_DELEG_NOTE_SEND_RAW: - str = gettext("Allow sending ONLY encrypted (raw) replication" - "\n\t\t\t\tstreams"); + str = gettext("Allow sending datasets, but only in 'raw'" + "\n\t\t\t\treplication mode"); + break; + case ZFS_DELEG_NOTE_SEND_ENCRYPTED: + str = gettext("Allow sending only encrypted datasets, and" + "\n\t\t\t\tonly in 'raw' replication mode"); break; case ZFS_DELEG_NOTE_SHARE: str = gettext("Allows sharing file systems over NFS or SMB" @@ -7775,6 +7782,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) } (void) fprintf(stderr, gettext("warning: %s not in" "/proc/self/mounts\n"), path); + /* libzfs-internal flags; umount2(2) rejects them */ + flags &= ~(MS_CRYPT | MS_OVERLAY); if ((ret = umount2(path, flags)) != 0) (void) fprintf(stderr, gettext("%s: %s\n"), path, strerror(errno)); diff --git a/sys/contrib/openzfs/cmd/zhack.c b/sys/contrib/openzfs/cmd/zhack.c index 36cf4f843d09..4ce6bb88da91 100644 --- a/sys/contrib/openzfs/cmd/zhack.c +++ b/sys/contrib/openzfs/cmd/zhack.c @@ -45,6 +45,7 @@ #include <sys/dsl_synctask.h> #include <sys/vdev.h> #include <sys/vdev_impl.h> +#include <sys/mmp.h> #include <sys/fs/zfs.h> #include <sys/dmu_objset.h> #include <sys/dsl_pool.h> @@ -110,6 +111,13 @@ usage(void) "\n" " <device> : path to vdev\n" "\n" + " mmp reclaim <pool>\n" + " import a pool whose MMP claim cannot reach every mirror\n" + " leg the config still expects, then mark the unreachable\n" + " leaves offline so ordinary imports succeed. Manual\n" + " recovery: fence the peer first, this cannot see a\n" + " live host whose legs are all invisible from here\n" + "\n" " metaslab leak <pool>\n" " apply allocation map from zdb to specified pool\n"); exit(1); @@ -600,6 +608,174 @@ zhack_do_action_idle(int argc, char **argv) spa_close(spa, FTAG); } +/* + * Collect the mirror legs this host could not open. vdev_not_present is set + * during import for any leaf whose open failed (vdev.c), and a leg in that + * state is what the relaxed claim forgives, so it is also what has to be + * marked offline for the ordinary imports which follow to succeed. + */ +static void +zhack_collect_absent(vdev_t *vd, uint64_t *guids, uint_t *n, uint_t max) +{ + /* + * Skip the same subtrees mmp_claim_uberblock_sync() skips. The claim + * never counts these, so offlining them buys nothing, and offlining a + * log leg would drag in spa_reset_logs(). Pruned at the interior node + * because the log flag lives on the top-level vdev. + */ + if (vd->vdev_islog || vd->vdev_isspare || vd->vdev_isl2cache || + vd->vdev_ishole || vd->vdev_ops == &vdev_indirect_ops) + return; + + for (uint64_t c = 0; c < vd->vdev_children; c++) + zhack_collect_absent(vd->vdev_child[c], guids, n, max); + + if (!vd->vdev_ops->vdev_op_leaf || !vd->vdev_not_present) + return; + + /* + * Take only the legs the relaxed claim can forgive, which are the + * direct children of a top-level mirror: the relaxation lives in the + * nparity == 0 branch and walks that vdev's children. A raidz or + * draid member is required as parity+1 in aggregate and never demanded + * individually, so an absent one does not raise the requirement and + * offlining it would be a persistent change that buys nothing. + */ + if (vd->vdev_parent != vd->vdev_top || + vdev_get_nparity(vd->vdev_top) != 0) + return; + + VERIFY3U(*n, <, max); + guids[(*n)++] = vd->vdev_guid; +} + +static int +zhack_do_mmp_reclaim(int argc, char **argv) +{ + spa_t *spa; + char *target; + uint64_t *guids; + uint_t nguids = 0, max; + int c, failed = 0; + + optind = 1; + while ((c = getopt(argc, argv, "+")) != -1) { + switch (c) { + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc < 1) { + (void) fprintf(stderr, "error: missing pool name\n"); + usage(); + } + target = argv[0]; + + /* + * Relax the claim for this import only. The write, the wait and the + * re-read are untouched, so a competing importer which shares any + * visibility with us is still refused. + */ + mmp_claim_relaxed = B_TRUE; + zhack_spa_open(target, B_FALSE, FTAG, &spa); + mmp_claim_relaxed = B_FALSE; + + /* + * Nothing to recover on a pool without multihost: no claim runs, so an + * ordinary import already succeeds with the absent leaves simply + * missing. Offlining them here would be a permanent change to a pool + * that never needed this tool. + */ + if (!spa_multihost(spa)) { + (void) fprintf(stderr, "%s: multihost is off, so no uberblock " + "claim runs and an ordinary import will succeed; refusing " + "to offline anything\n", target); + spa_close(spa, FTAG); + return (1); + } + + /* Takes SCL_VDEV itself, so size the array before we hold it. */ + max = MAX(vdev_count_leaves(spa), 1); + guids = umem_zalloc(max * sizeof (uint64_t), UMEM_NOFAIL); + + spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER); + zhack_collect_absent(spa->spa_root_vdev, guids, &nguids, max); + spa_config_exit(spa, SCL_VDEV, FTAG); + + if (nguids == 0) { + (void) fprintf(stdout, "%s: imported, no absent leaves to " + "mark offline\n", target); + } + + for (uint_t i = 0; i < nguids; i++) { + int error = vdev_offline(spa, guids[i], 0); + + if (error == 0) { + (void) fprintf(stdout, "%s: marked absent leaf %llu " + "offline\n", target, (u_longlong_t)guids[i]); + continue; + } + + failed++; + if (error == EBUSY) { + (void) fprintf(stderr, "%s: leaf %llu holds data no " + "other leaf has, left online\n", target, + (u_longlong_t)guids[i]); + } else { + (void) fprintf(stderr, "%s: could not offline leaf " + "%llu: %s\n", target, (u_longlong_t)guids[i], + strerror(error)); + } + } + + if (failed != 0) { + /* + * Not "the pool still needs zhack": this run exports cleanly + * under our own hostid, so the next import here skips the + * activity check entirely. The cost lands on the next host + * to take the pool, whose claim will count the leaves left + * online and refuse. + */ + (void) fprintf(stderr, "%s: %d absent leaves are still " + "online; a later import from another host will count " + "them and be refused\n", target, failed); + } + + umem_free(guids, max * sizeof (uint64_t)); + spa_close(spa, FTAG); + + return (failed == 0 ? 0 : 1); +} + +static int +zhack_do_mmp(int argc, char **argv) +{ + char *subcommand; + + argc--; + argv++; + if (argc == 0) { + (void) fprintf(stderr, + "error: no mmp operation specified\n"); + usage(); + } + + subcommand = argv[0]; + if (strcmp(subcommand, "reclaim") == 0) { + return (zhack_do_mmp_reclaim(argc, argv)); + } else { + (void) fprintf(stderr, "error: unknown subcommand: %s\n", + subcommand); + usage(); + } + + return (0); +} + static int zhack_do_action(int argc, char **argv) { @@ -1368,6 +1544,8 @@ main(int argc, char **argv) rv = zhack_do_action(argc, argv); } else if (strcmp(subcommand, "feature") == 0) { rv = zhack_do_feature(argc, argv); + } else if (strcmp(subcommand, "mmp") == 0) { + rv = zhack_do_mmp(argc, argv); } else if (strcmp(subcommand, "label") == 0) { return (zhack_do_label(argc, argv)); } else if (strcmp(subcommand, "metaslab") == 0) { diff --git a/sys/contrib/openzfs/config/kernel-follow-down-one.m4 b/sys/contrib/openzfs/config/kernel-follow-down-one.m4 deleted file mode 100644 index c4f7b99cebfd..000000000000 --- a/sys/contrib/openzfs/config/kernel-follow-down-one.m4 +++ /dev/null @@ -1,23 +0,0 @@ -dnl # SPDX-License-Identifier: CDDL-1.0 -dnl # -dnl # 2.6.38 API change -dnl # follow_down() renamed follow_down_one(). The original follow_down() -dnl # symbol still exists but will traverse down all the layers. -dnl # -AC_DEFUN([ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_ONE], [ - ZFS_LINUX_TEST_SRC([follow_down_one], [ - #include <linux/namei.h> - ],[ - struct path *p = NULL; - follow_down_one(p); - ]) -]) - -AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_DOWN_ONE], [ - AC_MSG_CHECKING([whether follow_down_one() is available]) - ZFS_LINUX_TEST_RESULT([follow_down_one], [ - AC_MSG_RESULT(yes) - ],[ - ZFS_LINUX_TEST_ERROR([follow_down_one()]) - ]) -]) diff --git a/sys/contrib/openzfs/config/kernel-follow-down.m4 b/sys/contrib/openzfs/config/kernel-follow-down.m4 new file mode 100644 index 000000000000..88d571f59504 --- /dev/null +++ b/sys/contrib/openzfs/config/kernel-follow-down.m4 @@ -0,0 +1,70 @@ +dnl # SPDX-License-Identifier: CDDL-1.0 +dnl # +dnl # 2.6.38 API change +dnl # follow_down() renamed follow_down_one(). The original follow_down() +dnl # symbol still exists but will traverse down all the layers. +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_ONE], [ + ZFS_LINUX_TEST_SRC([follow_down_one], [ + #include <linux/namei.h> + ],[ + struct path *p = NULL; + follow_down_one(p); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_DOWN_ONE], [ + AC_MSG_CHECKING([whether follow_down_one() is available]) + ZFS_LINUX_TEST_RESULT([follow_down_one], [ + AC_MSG_RESULT(yes) + ],[ + ZFS_LINUX_TEST_ERROR([follow_down_one()]) + ]) +]) + +dnl # +dnl # 6.3: look_down() gains a second flags param. Before this, we need +dnl # vfs_path_lookup() to build an emulation. +dnl # +dnl # 3.12: vfs_path_lookup unpublished from linux/namei.h, remains exported +dnl # 6.4: vfs_path_lookup republished in linux/namei.h +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_FLAGS], [ + ZFS_LINUX_TEST_SRC([follow_down_flags], [ + #include <linux/namei.h> + ],[ + struct path *p = NULL; + follow_down(p, 0); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_DOWN_FLAGS], [ + AC_MSG_CHECKING([whether follow_down() takes a flags parameter]) + ZFS_LINUX_TEST_RESULT([follow_down_flags], [ + AC_DEFINE(HAVE_FOLLOW_DOWN_FLAGS, 1, + [follow_down() takes a flags parameter]) + AC_MSG_RESULT(yes) + ],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING([whether vfs_path_lookup() is exported]) + ZFS_CHECK_SYMBOL_EXPORT( + [vfs_path_lookup], [fs/namei.c], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_VFS_PATH_LOOKUP_EXPORTED, 1, + [vfs_path_lookup() is exported]) + ],[ + AC_MSG_RESULT(no) + ZFS_LINUX_TEST_ERROR([follow_down()]) + ]) + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_SRC_FOLLOW_DOWN], [ + ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_ONE + ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_FLAGS +]) + +AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_DOWN], [ + ZFS_AC_KERNEL_FOLLOW_DOWN_ONE + ZFS_AC_KERNEL_FOLLOW_DOWN_FLAGS +]) diff --git a/sys/contrib/openzfs/config/kernel-fs-context.m4 b/sys/contrib/openzfs/config/kernel-fs-context.m4 index 317ddc21273d..542220e613c9 100644 --- a/sys/contrib/openzfs/config/kernel-fs-context.m4 +++ b/sys/contrib/openzfs/config/kernel-fs-context.m4 @@ -34,3 +34,28 @@ AC_DEFUN([ZFS_AC_KERNEL_FS_CONTEXT], [ ]) ]) ]) + +dnl # +dnl # 6.18 API change +dnl # vfs_parse_fs_string() 4th arg removed; length generated internally by +dnl # strlen(). +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_PARSE_FS_STRING_3ARGS], [ + ZFS_LINUX_TEST_SRC([vfs_parse_fs_string_3args], [ + #include <linux/fs.h> + #include <linux/fs_context.h> + ],[ + vfs_parse_fs_string(NULL, NULL, NULL); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_VFS_PARSE_FS_STRING_3ARGS], [ + AC_MSG_CHECKING([whether vfs_parse_fs_string() takes 3 args]) + ZFS_LINUX_TEST_RESULT([vfs_parse_fs_string_3args], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_VFS_PARSE_FS_STRING_3ARGS, 1, + [vfs_parse_fs_string() takes 3 args]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/sys/contrib/openzfs/config/kernel-idmap.m4 b/sys/contrib/openzfs/config/kernel-idmap.m4 new file mode 100644 index 000000000000..fd71fdb5d31c --- /dev/null +++ b/sys/contrib/openzfs/config/kernel-idmap.m4 @@ -0,0 +1,64 @@ +dnl # SPDX-License-Identifier: CDDL-1.0 +AC_DEFUN([ZFS_AC_KERNEL_SRC_IDMAP], [ + dnl # + dnl # 6.3 API change + dnl # The first arg is changed to struct mnt_idmap * + dnl # + ZFS_LINUX_TEST_SRC([generic_permission_idmap_mntidmap], [ + #include <linux/fs.h> + ], [ + struct mnt_idmap *idmap = NULL; + struct inode *inode = NULL; + int mask = 0; + int ret = generic_permission(idmap, inode, mask); + (void) ret; + ]) + + dnl # + dnl # 5.12 API change that added the struct user_namespace* arg + dnl # to the front of this function type's arg list. + dnl # + ZFS_LINUX_TEST_SRC([generic_permission_idmap_userns], [ + #include <linux/fs.h> + ], [ + struct user_namespace *userns = NULL; + struct inode *inode = NULL; + int mask = 0; + int ret = generic_permission(userns, inode, mask); + (void) ret; + ]) + + dnl # + dnl # Before 5.12 user namespace was part of kcred + dnl # + ZFS_LINUX_TEST_SRC([generic_permission_idmap_none], [ + #include <linux/fs.h> + ], [ + struct inode *inode = NULL; + int mask = 0; + int ret = generic_permission(inode, mask); + (void) ret; + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_IDMAP], [ + AC_MSG_CHECKING([for id mapping mechanism]) + ZFS_LINUX_TEST_RESULT([generic_permission_idmap_mntidmap], [ + AC_MSG_RESULT([mnt_idmap]) + AC_DEFINE(HAVE_IDMAP_MNTIDMAP, 1, + [id mapping mechanism is mnt_idmap]) + ],[ + ZFS_LINUX_TEST_RESULT([generic_permission_idmap_userns], [ + AC_MSG_RESULT([user_namespace]) + AC_DEFINE(HAVE_IDMAP_USERNS, 1, + [id mapping mechanism is user_namespace]) + ],[ + ZFS_LINUX_TEST_RESULT([generic_permission_idmap_none], [ + AC_MSG_RESULT([none]) + ],[ + AC_MSG_RESULT([unknown]) + ZFS_LINUX_TEST_ERROR([generic_permission]) + ]) + ]) + ]) +]) diff --git a/sys/contrib/openzfs/config/kernel-rename.m4 b/sys/contrib/openzfs/config/kernel-rename.m4 index 2f00da3a0c5c..4bfc54652c34 100644 --- a/sys/contrib/openzfs/config/kernel-rename.m4 +++ b/sys/contrib/openzfs/config/kernel-rename.m4 @@ -1,24 +1,6 @@ dnl # SPDX-License-Identifier: CDDL-1.0 AC_DEFUN([ZFS_AC_KERNEL_SRC_RENAME], [ dnl # - dnl # 4.9 API change, - dnl # - dnl # iops->rename2() merged into iops->rename(), and iops->rename() now - dnl # wants flags. - dnl # - ZFS_LINUX_TEST_SRC([inode_operations_rename_flags], [ - #include <linux/fs.h> - static int rename_fn(struct inode *sip, struct dentry *sdp, - struct inode *tip, struct dentry *tdp, - unsigned int flags) { return 0; } - - static const struct inode_operations - iops __attribute__ ((unused)) = { - .rename = rename_fn, - }; - ],[]) - - dnl # dnl # 5.12 API change, dnl # dnl # Linux 5.12 introduced passing struct user_namespace* as the first @@ -68,15 +50,6 @@ AC_DEFUN([ZFS_AC_KERNEL_RENAME], [ [iops->rename() takes struct user_namespace*]) ],[ AC_MSG_RESULT(no) - - AC_MSG_CHECKING([whether iops->rename() wants flags]) - ZFS_LINUX_TEST_RESULT([inode_operations_rename_flags], [ - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_RENAME_WANTS_FLAGS, 1, - [iops->rename() wants flags]) - ],[ - AC_MSG_RESULT(no) - ]) ]) ]) ]) diff --git a/sys/contrib/openzfs/config/kernel-sget-args.m4 b/sys/contrib/openzfs/config/kernel-sget-args.m4 deleted file mode 100644 index 1c8a8d3291be..000000000000 --- a/sys/contrib/openzfs/config/kernel-sget-args.m4 +++ /dev/null @@ -1,26 +0,0 @@ -dnl # SPDX-License-Identifier: CDDL-1.0 -dnl # -dnl # 3.6 API change, -dnl # 'sget' now takes the mount flags as an argument. -dnl # -AC_DEFUN([ZFS_AC_KERNEL_SRC_SGET], [ - ZFS_LINUX_TEST_SRC([sget_5args], [ - #include <linux/fs.h> - ],[ - struct file_system_type *type = NULL; - int (*test)(struct super_block *,void *) = NULL; - int (*set)(struct super_block *,void *) = NULL; - int flags = 0; - void *data = NULL; - (void) sget(type, test, set, flags, data); - ]) -]) - -AC_DEFUN([ZFS_AC_KERNEL_SGET], [ - AC_MSG_CHECKING([whether sget() wants 5 args]) - ZFS_LINUX_TEST_RESULT([sget_5args], [ - AC_MSG_RESULT(yes) - ],[ - ZFS_LINUX_TEST_ERROR([sget()]) - ]) -]) diff --git a/sys/contrib/openzfs/config/kernel-workqueue.m4 b/sys/contrib/openzfs/config/kernel-workqueue.m4 new file mode 100644 index 000000000000..546ec35f18e4 --- /dev/null +++ b/sys/contrib/openzfs/config/kernel-workqueue.m4 @@ -0,0 +1,77 @@ +dnl # SPDX-License-Identifier: CDDL-1.0 +dnl # +dnl # 5.19 API change +dnl # flush_workqueue() will no longer compile for system/global workqueues, +dnl # __flush_workqueue() introduced with no restrictions. +dnl # +dnl # 6.17 API change +dnl # schedule_delayed_work() now queues to system_percpu_wq, system_wq +dnl # deprecated. +dnl # +dnl # We cannot simply test for system_percpu_wq, as it has been backported +dnl # to LTS kernels where it is _not_ used for the delay queue. However, +dnl # the same commit that introduced it on mainline (a2be943b46b4a) also +dnl # added system_percpu_wq to the flush_workqueue() macro as one of the +dnl # global workqueues to reject at compiletime, and removed system_wq. +dnl # That part however, was _not_ backported, so we can use it to determine +dnl # which waitqueue is used for delay - if __flush_workqueue() accepts it, +dnl # but flush_workqueue() does not, then it is the delay workqueue. +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_FLUSH_DELAY_WORKQUEUE], [ + ZFS_LINUX_TEST_SRC([flush_workqueue_system], [ + #include <linux/workqueue.h> + ], [ + flush_workqueue(system_wq); + ]) + ZFS_LINUX_TEST_SRC([flush_workqueue_percpu], [ + #include <linux/workqueue.h> + ], [ + flush_workqueue(system_percpu_wq); + ]) + ZFS_LINUX_TEST_SRC([flush_workqueue_internal_system], [ + #include <linux/workqueue.h> + ], [ + __flush_workqueue(system_wq); + ]) + ZFS_LINUX_TEST_SRC([flush_workqueue_internal_percpu], [ + #include <linux/workqueue.h> + ], [ + __flush_workqueue(system_percpu_wq); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_FLUSH_DELAY_WORKQUEUE], [ + AC_MSG_CHECKING([how to flush delay workqueue]) + ZFS_LINUX_TEST_RESULT([flush_workqueue_internal_percpu], [ + dnl system_percpu_wq exists. the flush_workqueue() + dnl macro will reject the one used for delay + ZFS_LINUX_TEST_RESULT([flush_workqueue_percpu], [ + AC_MSG_RESULT([internal+system]) + AC_DEFINE(HAVE_FLUSH_DELAY_WORKQUEUE_INTERNAL, 1, + [use __flush_workqueue() to flush delay workqueue]) + ], [ + AC_MSG_RESULT([internal+percpu]) + AC_DEFINE(HAVE_FLUSH_DELAY_WORKQUEUE_INTERNAL, 1, + [use __flush_workqueue() to flush delay workqueue]) + AC_DEFINE(HAVE_FLUSH_DELAY_WORKQUEUE_PERCPU, 1, + [use system_delay_wq for delay workqueue]) + ]) + ], [ + dnl system_percpu_wq does not exist. use whichever did + dnl not reject it + ZFS_LINUX_TEST_RESULT([flush_workqueue_internal_system], [ + AC_MSG_RESULT([internal+system]) + AC_DEFINE(HAVE_FLUSH_DELAY_WORKQUEUE_INTERNAL, 1, + [use __flush_workqueue() to flush delay workqueue]) + ], [ + AC_MSG_RESULT([system]) + ]) + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_SRC_WORKQUEUE], [ + ZFS_AC_KERNEL_SRC_FLUSH_DELAY_WORKQUEUE +]) +AC_DEFUN([ZFS_AC_KERNEL_WORKQUEUE], [ + ZFS_AC_KERNEL_FLUSH_DELAY_WORKQUEUE +]) diff --git a/sys/contrib/openzfs/config/kernel.m4 b/sys/contrib/openzfs/config/kernel.m4 index 280f04e7ba50..69b07ae73f61 100644 --- a/sys/contrib/openzfs/config/kernel.m4 +++ b/sys/contrib/openzfs/config/kernel.m4 @@ -65,6 +65,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_SHRINKER ZFS_AC_KERNEL_SRC_MKDIR ZFS_AC_KERNEL_SRC_LOOKUP_FLAGS + ZFS_AC_KERNEL_SRC_IDMAP ZFS_AC_KERNEL_SRC_CREATE ZFS_AC_KERNEL_SRC_PERMISSION ZFS_AC_KERNEL_SRC_TMPFILE @@ -78,9 +79,9 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_SECURITY_INODE ZFS_AC_KERNEL_SRC_FS_CONTEXT ZFS_AC_KERNEL_SRC_FS_PARSE + ZFS_AC_KERNEL_SRC_VFS_PARSE_FS_STRING_3ARGS ZFS_AC_KERNEL_SRC_SB_DYING ZFS_AC_KERNEL_SRC_SET_NLINK - ZFS_AC_KERNEL_SRC_SGET ZFS_AC_KERNEL_SRC_VFS_FILEMAP_DIRTY_FOLIO ZFS_AC_KERNEL_SRC_VFS_READ_FOLIO ZFS_AC_KERNEL_SRC_VFS_MIGRATE_FOLIO @@ -97,7 +98,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_VFS_DEDUPE_FILE_RANGE ZFS_AC_KERNEL_SRC_KMAP_ATOMIC_ARGS ZFS_AC_KERNEL_SRC_KMAP_LOCAL_PAGE - ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_ONE + ZFS_AC_KERNEL_SRC_FOLLOW_DOWN ZFS_AC_KERNEL_SRC_MAKE_REQUEST_FN ZFS_AC_KERNEL_SRC_GENERIC_IO_ACCT ZFS_AC_KERNEL_SRC_FPU @@ -143,6 +144,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_INODE_GENERIC_DROP ZFS_AC_KERNEL_SRC_KASAN_ENABLED ZFS_AC_KERNEL_SRC_FILELOCK_HEADER + ZFS_AC_KERNEL_SRC_WORKQUEUE case "$host_cpu" in powerpc*) ZFS_AC_KERNEL_SRC_CPU_HAS_FEATURE @@ -188,6 +190,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_SHRINKER ZFS_AC_KERNEL_MKDIR ZFS_AC_KERNEL_LOOKUP_FLAGS + ZFS_AC_KERNEL_IDMAP ZFS_AC_KERNEL_CREATE ZFS_AC_KERNEL_PERMISSION ZFS_AC_KERNEL_TMPFILE @@ -201,9 +204,9 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_SECURITY_INODE ZFS_AC_KERNEL_FS_CONTEXT ZFS_AC_KERNEL_FS_PARSE + ZFS_AC_KERNEL_VFS_PARSE_FS_STRING_3ARGS ZFS_AC_KERNEL_SB_DYING ZFS_AC_KERNEL_SET_NLINK - ZFS_AC_KERNEL_SGET ZFS_AC_KERNEL_VFS_FILEMAP_DIRTY_FOLIO ZFS_AC_KERNEL_VFS_READ_FOLIO ZFS_AC_KERNEL_VFS_MIGRATE_FOLIO @@ -220,7 +223,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_VFS_DEDUPE_FILE_RANGE ZFS_AC_KERNEL_KMAP_ATOMIC_ARGS ZFS_AC_KERNEL_KMAP_LOCAL_PAGE - ZFS_AC_KERNEL_FOLLOW_DOWN_ONE + ZFS_AC_KERNEL_FOLLOW_DOWN ZFS_AC_KERNEL_MAKE_REQUEST_FN ZFS_AC_KERNEL_GENERIC_IO_ACCT ZFS_AC_KERNEL_FPU @@ -267,6 +270,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_INODE_GENERIC_DROP ZFS_AC_KERNEL_KASAN_ENABLED ZFS_AC_KERNEL_FILELOCK_HEADER + ZFS_AC_KERNEL_WORKQUEUE case "$host_cpu" in powerpc*) ZFS_AC_KERNEL_CPU_HAS_FEATURE diff --git a/sys/contrib/openzfs/config/zfs-meta.m4 b/sys/contrib/openzfs/config/zfs-meta.m4 index 72d424e2d478..fcd6d4e5bd28 100644 --- a/sys/contrib/openzfs/config/zfs-meta.m4 +++ b/sys/contrib/openzfs/config/zfs-meta.m4 @@ -71,16 +71,16 @@ AC_DEFUN([ZFS_AC_META], [ fi ZFS_META_RELEASE=_ZFS_AC_META_GETVAL([Release]); - if test ! -f ".nogitrelease" && git rev-parse --git-dir > /dev/null 2>&1; then + if test ! -f "$srcdir/.nogitrelease" && git -C "$srcdir" rev-parse --git-dir > /dev/null 2>&1; then _match="${ZFS_META_NAME}-${ZFS_META_VERSION}" - _alias=$(git describe --match=${_match} 2>/dev/null) + _alias=$(git -C "$srcdir" describe --match=${_match} 2>/dev/null) _release=$(echo ${_alias}|sed "s/${ZFS_META_NAME}//"|cut -f3- -d'-'|tr - _) if test -n "${_release}"; then ZFS_META_RELEASE=${_release} _zfs_ac_meta_type="git describe" else _match="${ZFS_META_NAME}-${ZFS_META_VERSION}-${ZFS_META_RELEASE}" - _alias=$(git describe --match=${_match} 2>/dev/null) + _alias=$(git -C "$srcdir" describe --match=${_match} 2>/dev/null) _release=$(echo ${_alias}|sed 's/${ZFS_META_NAME}//'|cut -f3- -d'-'|tr - _) if test -n "${_release}"; then ZFS_META_RELEASE=${_release} diff --git a/sys/contrib/openzfs/contrib/initramfs/hooks/zfs.in b/sys/contrib/openzfs/contrib/initramfs/hooks/zfs.in index 6cd7e6f1ea3b..dc318933271d 100755 --- a/sys/contrib/openzfs/contrib/initramfs/hooks/zfs.in +++ b/sys/contrib/openzfs/contrib/initramfs/hooks/zfs.in @@ -31,7 +31,7 @@ done # shellcheck disable=SC2050 if [ @LIBFETCH_DYNAMIC@ -gt 0 ]; then - find /lib/ -name "@LIBFETCH_SONAME@" | while read -r libfetch; do + find /lib/ -type f -name "@LIBFETCH_SONAME@" | while read -r libfetch; do copy_exec "$libfetch" done fi diff --git a/sys/contrib/openzfs/etc/systemd/system/zfs-import-cache.service.in b/sys/contrib/openzfs/etc/systemd/system/zfs-import-cache.service.in index fd822989da93..c7aa34ccbc8a 100644 --- a/sys/contrib/openzfs/etc/systemd/system/zfs-import-cache.service.in +++ b/sys/contrib/openzfs/etc/systemd/system/zfs-import-cache.service.in @@ -2,8 +2,10 @@ Description=Import ZFS pools by cache file Documentation=man:zpool(8) DefaultDependencies=no -Requires=systemd-udev-settle.service +Wants=systemd-udev-settle.service After=systemd-udev-settle.service +After=systemd-udev-trigger.service +After=systemd-modules-load.service After=cryptsetup.target After=multipathd.service After=systemd-remount-fs.service diff --git a/sys/contrib/openzfs/etc/systemd/system/zfs-import-scan.service.in b/sys/contrib/openzfs/etc/systemd/system/zfs-import-scan.service.in index c5dd45d87e68..b834767712ad 100644 --- a/sys/contrib/openzfs/etc/systemd/system/zfs-import-scan.service.in +++ b/sys/contrib/openzfs/etc/systemd/system/zfs-import-scan.service.in @@ -2,8 +2,10 @@ Description=Import ZFS pools by device scanning Documentation=man:zpool(8) DefaultDependencies=no -Requires=systemd-udev-settle.service +Wants=systemd-udev-settle.service After=systemd-udev-settle.service +After=systemd-udev-trigger.service +After=systemd-modules-load.service After=cryptsetup.target After=multipathd.service Before=zfs-import.target diff --git a/sys/contrib/openzfs/etc/systemd/system/zfs-mount.service.in b/sys/contrib/openzfs/etc/systemd/system/zfs-mount.service.in index 41c02c8238ca..466db9037322 100644 --- a/sys/contrib/openzfs/etc/systemd/system/zfs-mount.service.in +++ b/sys/contrib/openzfs/etc/systemd/system/zfs-mount.service.in @@ -2,7 +2,6 @@ Description=Mount ZFS filesystems Documentation=man:zfs(8) DefaultDependencies=no -After=systemd-udev-settle.service After=zfs-import.target After=systemd-remount-fs.service Before=local-fs.target diff --git a/sys/contrib/openzfs/etc/systemd/system/zfs-mount@.service.in b/sys/contrib/openzfs/etc/systemd/system/zfs-mount@.service.in index 0698fad12074..68476f15dbd6 100644 --- a/sys/contrib/openzfs/etc/systemd/system/zfs-mount@.service.in +++ b/sys/contrib/openzfs/etc/systemd/system/zfs-mount@.service.in @@ -2,7 +2,6 @@ Description=Mount ZFS filesystem %I Documentation=man:zfs(8) DefaultDependencies=no -After=systemd-udev-settle.service After=zfs-import.target After=zfs-mount.service After=systemd-remount-fs.service diff --git a/sys/contrib/openzfs/etc/systemd/system/zfs-volume-wait.service.in b/sys/contrib/openzfs/etc/systemd/system/zfs-volume-wait.service.in index 110c0f5f52ee..e6265f1c9d32 100644 --- a/sys/contrib/openzfs/etc/systemd/system/zfs-volume-wait.service.in +++ b/sys/contrib/openzfs/etc/systemd/system/zfs-volume-wait.service.in @@ -1,7 +1,6 @@ [Unit] Description=Wait for ZFS Volume (zvol) links in /dev DefaultDependencies=no -After=systemd-udev-settle.service After=zfs-import.target ConditionPathIsDirectory=/sys/module/zfs diff --git a/sys/contrib/openzfs/include/os/freebsd/spl/sys/policy.h b/sys/contrib/openzfs/include/os/freebsd/spl/sys/policy.h index 48bc4f3d5b0f..a096c8cf779e 100644 --- a/sys/contrib/openzfs/include/os/freebsd/spl/sys/policy.h +++ b/sys/contrib/openzfs/include/os/freebsd/spl/sys/policy.h @@ -37,7 +37,6 @@ struct mount; struct vattr; struct znode; -int secpolicy_nfs(cred_t *cr); int secpolicy_zfs(cred_t *crd); int secpolicy_sys_config(cred_t *cr, int checkonly); int secpolicy_zinject(cred_t *cr); diff --git a/sys/contrib/openzfs/include/os/freebsd/zfs/sys/zfs_vfsops_os.h b/sys/contrib/openzfs/include/os/freebsd/zfs/sys/zfs_vfsops_os.h index 3ed311d49cc6..be004f6cf004 100644 --- a/sys/contrib/openzfs/include/os/freebsd/zfs/sys/zfs_vfsops_os.h +++ b/sys/contrib/openzfs/include/os/freebsd/zfs/sys/zfs_vfsops_os.h @@ -72,6 +72,7 @@ struct zfsvfs { int z_norm; /* normalization flags */ boolean_t z_atime; /* enable atimes mount option */ boolean_t z_unmounted; /* unmounted */ + boolean_t z_use_hold; /* held via dmu_objset_hold */ zfs_teardown_lock_t z_teardown_lock; zfs_teardown_inactive_lock_t z_teardown_inactive_lock; list_t z_all_znodes; /* all vnodes in the fs */ @@ -225,6 +226,7 @@ extern int zfs_resume_fs(zfsvfs_t *zfsvfs, struct dsl_dataset *ds); extern int zfs_end_fs(zfsvfs_t *zfsvfs, struct dsl_dataset *ds); extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers); extern int zfsvfs_create(const char *name, boolean_t readonly, zfsvfs_t **zfvp); +extern int zfsvfs_create_hold(const char *name, zfsvfs_t **zfvp); extern int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os); extern void zfsvfs_free(zfsvfs_t *zfsvfs); extern int zfs_check_global_label(const char *dsname, const char *hexsl); diff --git a/sys/contrib/openzfs/include/os/linux/Makefile.am b/sys/contrib/openzfs/include/os/linux/Makefile.am index c8eab5b9874b..083ccd49f955 100644 --- a/sys/contrib/openzfs/include/os/linux/Makefile.am +++ b/sys/contrib/openzfs/include/os/linux/Makefile.am @@ -17,6 +17,7 @@ kernel_linux_HEADERS = \ %D%/kernel/linux/simd_x86.h \ %D%/kernel/linux/utsname_compat.h \ %D%/kernel/linux/vfs_compat.h \ + %D%/kernel/linux/workqueue_compat.h \ %D%/kernel/linux/xattr_compat.h kernel_sysdir = $(kerneldir)/sys diff --git a/sys/contrib/openzfs/include/os/linux/kernel/linux/vfs_compat.h b/sys/contrib/openzfs/include/os/linux/kernel/linux/vfs_compat.h index 24219637d266..d79e07a673bb 100644 --- a/sys/contrib/openzfs/include/os/linux/kernel/linux/vfs_compat.h +++ b/sys/contrib/openzfs/include/os/linux/kernel/linux/vfs_compat.h @@ -24,6 +24,7 @@ * Copyright (C) 2011 Lawrence Livermore National Security, LLC. * Copyright (C) 2015 Jörg Thalheim. * Copyright (c) 2025, Rob Norris <robn@despairlabs.com> + * Copyright (c) 2026, TrueNAS. */ #ifndef _ZFS_VFS_H @@ -277,4 +278,49 @@ zpl_is_32bit_api(void) #define inode_state_read_once(ip) READ_ONCE(ip->i_state) #endif +/* 6.18 compat: 4th arg removed; function will do strlen() internally. */ +#ifdef HAVE_VFS_PARSE_FS_STRING_3ARGS +#define zpl_vfs_parse_fs_string(fc, key, val) \ + vfs_parse_fs_string((fc), (key), (val)) +#else +#define zpl_vfs_parse_fs_string(fc, key, val) \ + vfs_parse_fs_string((fc), (key), (val), (val != NULL) ? strlen(val) : 0) +#endif + +#if defined(HAVE_FOLLOW_DOWN_FLAGS) +static inline int +zpl_follow_down(struct path *path, unsigned int flags) +{ + return (follow_down(path, flags)); +} +#elif defined(HAVE_VFS_PATH_LOOKUP_EXPORTED) +/* + * vfs_path_lookup() has always been exported, but not always published. + */ +extern int vfs_path_lookup(struct dentry *, struct vfsmount *, + const char *, unsigned int, struct path *); +static inline int +zpl_follow_down(struct path *path, unsigned int flags) +{ + /* + * Simulate follow_down() by asking for a name lookup for "/" under the + * given path. + */ + struct path opath; + int err = vfs_path_lookup(path->dentry, path->mnt, "/", + LOOKUP_DOWN|flags, &opath); + if (err == 0) { + /* Trade the original path for the new one. */ + path_put(path); + path->dentry = opath.dentry; + path->mnt = opath.mnt; + path_get(path); + path_put(&opath); + } + return (err); +} +#else +#error "Unsupported kernel: no fallback implementation for follow_down()" +#endif + #endif /* _ZFS_VFS_H */ diff --git a/sys/contrib/openzfs/include/os/linux/kernel/linux/workqueue_compat.h b/sys/contrib/openzfs/include/os/linux/kernel/linux/workqueue_compat.h new file mode 100644 index 000000000000..3b7d4ef0f093 --- /dev/null +++ b/sys/contrib/openzfs/include/os/linux/kernel/linux/workqueue_compat.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: CDDL-1.0 +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2026, TrueNAS. + */ + +#ifndef _ZFS_WORKQUEUE_COMPAT_H +#define _ZFS_WORKQUEUE_COMPAT_H + +/* + * Forcibly flush the kernel's "delayed work" workqueue. + */ +#ifdef HAVE_FLUSH_DELAY_WORKQUEUE_INTERNAL +/* + * Linux 5.19: flush_workqueue() on system workqueues is a compile error, use + * __flush_workqueue() instead. + */ +#ifdef HAVE_FLUSH_DELAY_WORKQUEUE_PERCPU +/* + * Linux 6.17: system_percpu_wq introduced as direct replacement for system_wq, + * and takes over as the delay workqueue. + */ +#define zpl_flush_delay_workqueue() __flush_workqueue(system_percpu_wq) +#else +#define zpl_flush_delay_workqueue() __flush_workqueue(system_wq) +#endif +#else +#define zpl_flush_delay_workqueue() flush_workqueue(system_wq) +#endif + +#endif diff --git a/sys/contrib/openzfs/include/os/linux/zfs/sys/abd_os.h b/sys/contrib/openzfs/include/os/linux/zfs/sys/abd_os.h index 8c4d5b7e1073..e214012ba930 100644 --- a/sys/contrib/openzfs/include/os/linux/zfs/sys/abd_os.h +++ b/sys/contrib/openzfs/include/os/linux/zfs/sys/abd_os.h @@ -48,14 +48,6 @@ typedef int abd_iter_page_func_t(struct page *, size_t, size_t, void *); int abd_iterate_page_func(struct abd *, size_t, size_t, abd_iter_page_func_t *, void *); -/* - * Linux ABD bio functions - * Note: these are only needed to support vdev_classic. See comment in - * vdev_disk.c. - */ -unsigned int abd_bio_map_off(struct bio *, struct abd *, unsigned int, size_t); -unsigned long abd_nr_pages_off(struct abd *, unsigned int, size_t); - __attribute__((malloc)) struct abd *abd_alloc_from_pages(struct page **, unsigned long, uint64_t); diff --git a/sys/contrib/openzfs/include/os/linux/zfs/sys/policy.h b/sys/contrib/openzfs/include/os/linux/zfs/sys/policy.h index 8fa6ab01d1ad..8895de110e7a 100644 --- a/sys/contrib/openzfs/include/os/linux/zfs/sys/policy.h +++ b/sys/contrib/openzfs/include/os/linux/zfs/sys/policy.h @@ -38,7 +38,6 @@ struct znode; -int secpolicy_nfs(const cred_t *); int secpolicy_sys_config(const cred_t *, boolean_t); int secpolicy_vnode_access2(const cred_t *, struct inode *, uid_t, mode_t, mode_t); diff --git a/sys/contrib/openzfs/include/os/linux/zfs/sys/zfs_vfsops_os.h b/sys/contrib/openzfs/include/os/linux/zfs/sys/zfs_vfsops_os.h index 123ea71b4de9..2058ef322945 100644 --- a/sys/contrib/openzfs/include/os/linux/zfs/sys/zfs_vfsops_os.h +++ b/sys/contrib/openzfs/include/os/linux/zfs/sys/zfs_vfsops_os.h @@ -100,6 +100,7 @@ struct zfsvfs { int z_norm; /* normalization flags */ boolean_t z_relatime; /* enable relatime mount option */ boolean_t z_unmounted; /* unmounted */ + boolean_t z_use_hold; /* held via dmu_objset_hold */ rrmlock_t z_teardown_lock; krwlock_t z_teardown_inactive_lock; list_t z_all_znodes; /* all znodes in the fs */ @@ -237,6 +238,7 @@ extern int zfs_end_fs(zfsvfs_t *zfsvfs, struct dsl_dataset *ds); extern void zfs_exit_fs(zfsvfs_t *zfsvfs); extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers); extern int zfsvfs_create(const char *name, boolean_t readony, zfsvfs_t **zfvp); +extern int zfsvfs_create_hold(const char *name, zfsvfs_t **zfvp); extern int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os); extern void zfsvfs_free(zfsvfs_t *zfsvfs); extern int zfs_check_global_label(const char *dsname, const char *hexsl); diff --git a/sys/contrib/openzfs/include/sys/dnode.h b/sys/contrib/openzfs/include/sys/dnode.h index b9ded73efe33..78dd0dc4f37b 100644 --- a/sys/contrib/openzfs/include/sys/dnode.h +++ b/sys/contrib/openzfs/include/sys/dnode.h @@ -449,6 +449,8 @@ void dnode_diduse_space(dnode_t *dn, int64_t space); void dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx, boolean_t have_read, boolean_t force); uint64_t dnode_block_freed(dnode_t *dn, uint64_t blkid); +uint64_t dnode_block_freed_after(dnode_t *dn, uint64_t blkid, + uint64_t override_txg); void dnode_init(void); void dnode_fini(void); int dnode_next_offset(dnode_t *dn, int flags, uint64_t *off, diff --git a/sys/contrib/openzfs/include/sys/dsl_deleg.h b/sys/contrib/openzfs/include/sys/dsl_deleg.h index 36dd6211219d..b91b64731612 100644 --- a/sys/contrib/openzfs/include/sys/dsl_deleg.h +++ b/sys/contrib/openzfs/include/sys/dsl_deleg.h @@ -47,6 +47,7 @@ extern "C" { #define ZFS_DELEG_PERM_SHARE "share" #define ZFS_DELEG_PERM_SEND "send" #define ZFS_DELEG_PERM_SEND_RAW "send:raw" +#define ZFS_DELEG_PERM_SEND_ENCRYPTED "send:encrypted" #define ZFS_DELEG_PERM_RECEIVE "receive" #define ZFS_DELEG_PERM_RECEIVE_APPEND "receive:append" #define ZFS_DELEG_PERM_ALLOW "allow" diff --git a/sys/contrib/openzfs/include/sys/mmp.h b/sys/contrib/openzfs/include/sys/mmp.h index 239170f46b31..7456c17e1631 100644 --- a/sys/contrib/openzfs/include/sys/mmp.h +++ b/sys/contrib/openzfs/include/sys/mmp.h @@ -74,6 +74,11 @@ extern uint64_t zfs_multihost_interval; extern uint_t zfs_multihost_fail_intervals; extern uint_t zfs_multihost_import_intervals; +#ifndef _KERNEL +/* Manual recovery only, see the comment in mmp.c. Never in the module. */ +extern boolean_t mmp_claim_relaxed; +#endif + #ifdef __cplusplus } #endif diff --git a/sys/contrib/openzfs/include/sys/vdev.h b/sys/contrib/openzfs/include/sys/vdev.h index 131cfc9cd16b..94e19f4d5c21 100644 --- a/sys/contrib/openzfs/include/sys/vdev.h +++ b/sys/contrib/openzfs/include/sys/vdev.h @@ -25,6 +25,7 @@ * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright (c) 2017, Intel Corporation. * Copyright (c) 2019, Datto Inc. All rights reserved. + * Copyright (c) 2026, TrueNAS. */ #ifndef _SYS_VDEV_H @@ -56,9 +57,10 @@ typedef boolean_t vdev_open_children_func_t(vdev_t *vd); extern void vdev_dbgmsg(vdev_t *vd, const char *fmt, ...) __attribute__((format(__printf__, 2, 3))); extern void vdev_dbgmsg_print_tree(vdev_t *, int); -extern int vdev_open(vdev_t *); -extern void vdev_open_children(vdev_t *); -extern void vdev_open_children_subset(vdev_t *, vdev_open_children_func_t *); +extern int vdev_open(vdev_t *, cred_t *); +extern void vdev_open_children(vdev_t *, cred_t *); +extern void vdev_open_children_subset(vdev_t *, cred_t *, + vdev_open_children_func_t *); extern int vdev_validate(vdev_t *); extern int vdev_copy_path_strict(vdev_t *, vdev_t *); extern void vdev_copy_path_relaxed(vdev_t *, vdev_t *); diff --git a/sys/contrib/openzfs/include/sys/vdev_impl.h b/sys/contrib/openzfs/include/sys/vdev_impl.h index afaa401343d9..e44dc3da53f5 100644 --- a/sys/contrib/openzfs/include/sys/vdev_impl.h +++ b/sys/contrib/openzfs/include/sys/vdev_impl.h @@ -67,7 +67,7 @@ typedef int vdev_init_func_t(spa_t *spa, nvlist_t *nv, void **tsd); typedef void vdev_kobj_post_evt_func_t(vdev_t *vd); typedef void vdev_fini_func_t(vdev_t *vd); typedef int vdev_open_func_t(vdev_t *vd, uint64_t *size, uint64_t *max_size, - uint64_t *ashift, uint64_t *pshift); + uint64_t *ashift, uint64_t *pshift, cred_t *cr); typedef void vdev_close_func_t(vdev_t *vd); typedef uint64_t vdev_asize_func_t(vdev_t *vd, uint64_t psize, uint64_t txg); typedef uint64_t vdev_min_asize_func_t(vdev_t *vd); diff --git a/sys/contrib/openzfs/include/sys/zfs_file.h b/sys/contrib/openzfs/include/sys/zfs_file.h index 67abe9988aaa..178b224a4293 100644 --- a/sys/contrib/openzfs/include/sys/zfs_file.h +++ b/sys/contrib/openzfs/include/sys/zfs_file.h @@ -41,7 +41,8 @@ typedef struct zfs_file_attr { mode_t zfa_mode; /* file type */ } zfs_file_attr_t; -int zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fp); +int zfs_file_open(const char *path, int flags, int mode, cred_t *cr, + zfs_file_t **fp); void zfs_file_close(zfs_file_t *fp); int zfs_file_write(zfs_file_t *fp, const void *buf, size_t len, ssize_t *resid); diff --git a/sys/contrib/openzfs/include/zfs_crrd.h b/sys/contrib/openzfs/include/zfs_crrd.h index ba192a2062ea..669647aba6e4 100644 --- a/sys/contrib/openzfs/include/zfs_crrd.h +++ b/sys/contrib/openzfs/include/zfs_crrd.h @@ -60,16 +60,17 @@ typedef struct { rrd_t dbr_months; } dbrrd_t; -size_t rrd_len(rrd_t *rrd); +size_t rrd_len(const rrd_t *rrd); -const rrd_data_t *rrd_entry(rrd_t *r, size_t i); +const rrd_data_t *rrd_entry(const rrd_t *r, size_t i); rrd_data_t *rrd_tail_entry(rrd_t *rrd); uint64_t rrd_tail(rrd_t *rrd); -uint64_t rrd_get(rrd_t *rrd, size_t i); +uint64_t rrd_get(const rrd_t *rrd, size_t i); void rrd_add(rrd_t *rrd, hrtime_t time, uint64_t txg); void dbrrd_add(dbrrd_t *db, hrtime_t time, uint64_t txg); uint64_t dbrrd_query(dbrrd_t *r, hrtime_t tv, dbrrd_rounding_t rouding); +hrtime_t dbrrd_latest_time(dbrrd_t *r); #endif diff --git a/sys/contrib/openzfs/include/zfs_deleg.h b/sys/contrib/openzfs/include/zfs_deleg.h index a7bbf1620ad5..aecab3816895 100644 --- a/sys/contrib/openzfs/include/zfs_deleg.h +++ b/sys/contrib/openzfs/include/zfs_deleg.h @@ -56,6 +56,7 @@ typedef enum { ZFS_DELEG_NOTE_RENAME, ZFS_DELEG_NOTE_SEND, ZFS_DELEG_NOTE_SEND_RAW, + ZFS_DELEG_NOTE_SEND_ENCRYPTED, ZFS_DELEG_NOTE_RECEIVE, ZFS_DELEG_NOTE_ALLOW, ZFS_DELEG_NOTE_USERPROP, diff --git a/sys/contrib/openzfs/lib/libefi/rdwr_efi.c b/sys/contrib/openzfs/lib/libefi/rdwr_efi.c index 0a93fd1338f6..df1c6d87e536 100644 --- a/sys/contrib/openzfs/lib/libefi/rdwr_efi.c +++ b/sys/contrib/openzfs/lib/libefi/rdwr_efi.c @@ -410,8 +410,8 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc) /* figure out the number of entries that would fit into 16K */ nparts = EFI_MIN_ARRAY_SIZE / sizeof (efi_gpe_t); - length = (int) sizeof (struct dk_gpt) + - (int) sizeof (struct dk_part) * (nparts - 1); + length = (int)(sizeof (struct dk_gpt)) + + (int)(sizeof (struct dk_part)) * (nparts - 1); vptr = calloc(1, length); if (vptr == NULL) @@ -422,8 +422,8 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc) if ((rval == VT_EINVAL) && vptr->efi_nparts > nparts) { void *tmp; - length = (int) sizeof (struct dk_gpt) + - (int) sizeof (struct dk_part) * (vptr->efi_nparts - 1); + length = (int)(sizeof (struct dk_gpt)) + + (int)(sizeof (struct dk_part)) * (vptr->efi_nparts - 1); if ((tmp = realloc(vptr, length)) == NULL) { /* cppcheck-suppress doubleFree */ free(vptr); @@ -695,7 +695,7 @@ efi_read(int fd, struct dk_gpt *vtoc) if (NBLOCKS(vtoc->efi_nparts, disk_info.dki_lbsize) < 34) { label_len = EFI_MIN_ARRAY_SIZE + disk_info.dki_lbsize; } else { - label_len = vtoc->efi_nparts * (int) sizeof (efi_gpe_t) + + label_len = vtoc->efi_nparts * (int)(sizeof (efi_gpe_t)) + disk_info.dki_lbsize; if (label_len % disk_info.dki_lbsize) { /* pad to physical sector size */ diff --git a/sys/contrib/openzfs/lib/libshare/os/linux/smb.c b/sys/contrib/openzfs/lib/libshare/os/linux/smb.c index 40996ecc8f89..cc1b57f7dfa4 100644 --- a/sys/contrib/openzfs/lib/libshare/os/linux/smb.c +++ b/sys/contrib/openzfs/lib/libshare/os/linux/smb.c @@ -132,8 +132,9 @@ smb_retrieve_shares(void) continue; /* Trim trailing new-line character(s). */ - while (line[strlen(line) - 1] == '\r' || - line[strlen(line) - 1] == '\n') + while ((line[0] != '\0') && + (line[strlen(line) - 1] == '\r' || + line[strlen(line) - 1] == '\n')) line[strlen(line) - 1] = '\0'; /* Split the line in two, separated by '=' */ diff --git a/sys/contrib/openzfs/lib/libspl/include/assert.h b/sys/contrib/openzfs/lib/libspl/include/assert.h index e704a899e748..e3a438e009a6 100644 --- a/sys/contrib/openzfs/lib/libspl/include/assert.h +++ b/sys/contrib/openzfs/lib/libspl/include/assert.h @@ -219,6 +219,13 @@ do { \ (void *)__left, __VA_ARGS__); \ } while (0) +#define VERIFY_IMPLY(A, B) \ + ((void)(((!(A)) || (B)) || \ + libspl_assert("(" #A ") implies (" #B ")", \ + __FILE__, __FUNCTION__, __LINE__))) + +#define VERIFY_EQUIV(A, B) VERIFY3B(A, ==, B) + #ifdef assert #undef assert #endif @@ -264,11 +271,8 @@ do { \ #define ASSERT VERIFY #define ASSERTF VERIFYF #define assert VERIFY -#define IMPLY(A, B) \ - ((void)(((!(A)) || (B)) || \ - libspl_assert("(" #A ") implies (" #B ")", \ - __FILE__, __FUNCTION__, __LINE__))) -#define EQUIV(A, B) VERIFY3B(A, ==, B) +#define IMPLY VERIFY_IMPLY +#define EQUIV VERIFY_EQUIV #endif /* NDEBUG */ diff --git a/sys/contrib/openzfs/lib/libspl/include/os/freebsd/sys/mount.h b/sys/contrib/openzfs/lib/libspl/include/os/freebsd/sys/mount.h index 231c250d3410..9f3321a88ccf 100644 --- a/sys/contrib/openzfs/lib/libspl/include/os/freebsd/sys/mount.h +++ b/sys/contrib/openzfs/lib/libspl/include/os/freebsd/sys/mount.h @@ -89,17 +89,12 @@ #endif /* MNT_DETACH */ /* - * Overlay mount is default in Linux, but for solaris/zfs - * compatibility, MS_OVERLAY is defined to explicitly have the user - * provide a flag (-O) to mount over a non empty directory. + * MS_OVERLAY (-O: allow mounting over a non-empty directory) and MS_CRYPT + * (load/unload encryption keys with the (un)mount) are libzfs-internal flags, + * never passed to the kernel. They use high bits that do_unmount() masks + * off before unmount(2); do_mount() builds its iovec from named options. */ -#define MS_OVERLAY 0x00000004 - -/* - * MS_CRYPT indicates that encryption keys should be loaded if they are not - * already available. This is not defined in glibc, but it is never seen by - * the kernel so it will not cause any problems. - */ -#define MS_CRYPT 0x00000008 +#define MS_OVERLAY 0x20000000 +#define MS_CRYPT 0x40000000 #endif /* _LIBSPL_SYS_MOUNT_H */ diff --git a/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/mount.h b/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/mount.h index c4a291f5c22d..80679564aa4d 100644 --- a/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/mount.h +++ b/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/mount.h @@ -83,17 +83,13 @@ #endif /* MNT_DETACH */ /* - * Overlay mount is default in Linux, but for solaris/zfs - * compatibility, MS_OVERLAY is defined to explicitly have the user - * provide a flag (-O) to mount over a non empty directory. + * MS_OVERLAY (-O: allow mounting over a non-empty directory) and MS_CRYPT + * (load/unload encryption keys with the (un)mount) are libzfs-internal flags, + * never passed to the kernel. They use high bits because the old 0x4/0x8 + * aliased the umount2(2) flags MNT_EXPIRE/UMOUNT_NOFOLLOW; do_unmount() masks + * them off before the syscall. */ -#define MS_OVERLAY 0x00000004 - -/* - * MS_CRYPT indicates that encryption keys should be loaded if they are not - * already available. This is not defined in glibc, but it is never seen by - * the kernel so it will not cause any problems. - */ -#define MS_CRYPT 0x00000008 +#define MS_OVERLAY 0x20000000 +#define MS_CRYPT 0x40000000 #endif /* _LIBSPL_SYS_MOUNT_H */ diff --git a/sys/contrib/openzfs/lib/libzfs/libzfs_pool.c b/sys/contrib/openzfs/lib/libzfs/libzfs_pool.c index 42a1ddcf28a6..40c46436e1e3 100644 --- a/sys/contrib/openzfs/lib/libzfs/libzfs_pool.c +++ b/sys/contrib/openzfs/lib/libzfs/libzfs_pool.c @@ -2270,10 +2270,21 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, const char *hostname = "<unknown>"; uint64_t hostid = 0; mmp_state_t mmp_state; + uint32_t mmp_result = 0; mmp_state = fnvlist_lookup_uint64(nvinfo, ZPOOL_CONFIG_MMP_STATE); + /* + * A kernel which does not report a cause + * leaves this zero, which falls through to + * the messages below. + */ + if (nvlist_exists(nvinfo, + ZPOOL_CONFIG_MMP_RESULT)) + mmp_result = fnvlist_lookup_uint32( + nvinfo, ZPOOL_CONFIG_MMP_RESULT); + if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_HOSTNAME)) hostname = fnvlist_lookup_string(nvinfo, @@ -2284,7 +2295,23 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, hostid = fnvlist_lookup_uint64(nvinfo, ZPOOL_CONFIG_MMP_HOSTID); - if (mmp_state == MMP_STATE_ACTIVE) { + if (mmp_result == ENODEV) { + (void) snprintf(aux, sizeof (aux), + dgettext(TEXT_DOMAIN, "the multi" + "host claim could not be written " + "to a device\nthe pool " + "configuration expects to be " + "present.\nIf the device is " + "permanently gone, recover with " + "'zhack mmp reclaim'.")); + } else if (mmp_result == EIO) { + (void) snprintf(aux, sizeof (aux), + dgettext(TEXT_DOMAIN, "I/O errors " + "occurred while writing the multi" + "host claim.\nClear the device " + "errors, then run 'zpool " + "import'.")); + } else if (mmp_state == MMP_STATE_ACTIVE) { (void) snprintf(aux, sizeof (aux), dgettext(TEXT_DOMAIN, "pool is imp" "orted on host '%s' (hostid=%lx).\n" @@ -4509,6 +4536,7 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, uint64_t value; char buf[PATH_BUF_LEN]; char tmpbuf[PATH_BUF_LEN * 2]; + char rpath[MAXPATHLEN]; /* * vdev_name will be "root"/"root-0" for the root vdev, but it is the @@ -4534,12 +4562,8 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, path = tpath; if (name_flags & VDEV_NAME_FOLLOW_LINKS) { - char *rp = realpath(path, NULL); - if (rp) { - strlcpy(buf, rp, sizeof (buf)); - path = buf; - free(rp); - } + if (realpath(path, rpath) != NULL) + path = rpath; } /* @@ -5275,9 +5299,10 @@ zpool_load_compat(const char *compat, boolean_t *features, char *report, ZC_MMAP_FLAGS, featfd, 0); (void) close(featfd); - /* map ok, and last character == newline? */ + /* need map ok, and last character == newline */ if (fc == MAP_FAILED || fc[fs.st_size - 1] != '\n') { - (void) munmap((void *) fc, fs.st_size); + if (fc != MAP_FAILED) + (void) munmap((void *) fc, fs.st_size); strlcat(err_badfile, file, ZFS_MAXPROPLEN); strlcat(err_badfile, " ", ZFS_MAXPROPLEN); ret_badfile = B_TRUE; diff --git a/sys/contrib/openzfs/lib/libzfs/libzfs_sendrecv.c b/sys/contrib/openzfs/lib/libzfs/libzfs_sendrecv.c index 2c38333e18a1..b7bdf63a28d0 100644 --- a/sys/contrib/openzfs/lib/libzfs/libzfs_sendrecv.c +++ b/sys/contrib/openzfs/lib/libzfs/libzfs_sendrecv.c @@ -1976,8 +1976,10 @@ zfs_send_resume_impl_cb_impl(libzfs_handle_t *hdl, sendflags_t *flags, } char errbuf[ERRBUFLEN]; + char sendname[ZFS_MAX_DATASET_NAME_LEN]; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "warning: cannot send '%s'"), zhp->zfs_name); + (void) strlcpy(sendname, zhp->zfs_name, sizeof (sendname)); zfs_close(zhp); @@ -1989,7 +1991,7 @@ zfs_send_resume_impl_cb_impl(libzfs_handle_t *hdl, sendflags_t *flags, "source key must be loaded")); return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); case ESRCH: - if (lzc_exists(zhp->zfs_name)) { + if (lzc_exists(sendname)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "incremental source could not be found")); } @@ -4321,7 +4323,7 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, newname); if (nvlist_lookup_string(attrs, ZPROP_SOURCE, &source) == 0 && - strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0) + strcmp(source, fsname) == 0) continue; } /* diff --git a/sys/contrib/openzfs/lib/libzfs/libzfs_util.c b/sys/contrib/openzfs/lib/libzfs/libzfs_util.c index 26f5135dff62..dcc38969909f 100644 --- a/sys/contrib/openzfs/lib/libzfs/libzfs_util.c +++ b/sys/contrib/openzfs/lib/libzfs/libzfs_util.c @@ -2399,6 +2399,8 @@ zpool_prepare_disk(zpool_handle_t *zhp, nvlist_t *vdev_nv, &enc_sysfs_path); upath = zfs_get_underlying_path(path); + if (upath == NULL && path != NULL) + upath = strdup(path); pool_name = zhp ? zpool_get_name(zhp) : NULL; env = zpool_vdev_script_alloc_env(pool_name, path, upath, diff --git a/sys/contrib/openzfs/lib/libzfs/os/freebsd/libzfs_zmount.c b/sys/contrib/openzfs/lib/libzfs/os/freebsd/libzfs_zmount.c index 3723769d4e30..99227414fdbf 100644 --- a/sys/contrib/openzfs/lib/libzfs/os/freebsd/libzfs_zmount.c +++ b/sys/contrib/openzfs/lib/libzfs/os/freebsd/libzfs_zmount.c @@ -111,6 +111,13 @@ int do_unmount(zfs_handle_t *zhp, const char *mntpt, int flags) { (void) zhp; + + /* + * MS_CRYPT and MS_OVERLAY are libzfs-internal flags; keep them out of + * the unmount(2) syscall. MS_FORCE/MS_DETACH are real flags and kept. + */ + flags &= ~(MS_CRYPT | MS_OVERLAY); + if (unmount(mntpt, flags) < 0) return (errno); return (0); diff --git a/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_mount_os.c b/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_mount_os.c index 8ea72f462048..62349622a94b 100644 --- a/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_mount_os.c +++ b/sys/contrib/openzfs/lib/libzfs/os/linux/libzfs_mount_os.c @@ -386,6 +386,13 @@ do_unmount(zfs_handle_t *zhp, const char *mntpt, int flags) { (void) zhp; + /* + * MS_CRYPT and MS_OVERLAY are libzfs-internal flags; strip them so they + * are never passed to umount2(2), which rejects unknown flag bits with + * EINVAL. MS_FORCE/MS_DETACH are real umount2(2) flags and kept. + */ + flags &= ~(MS_CRYPT | MS_OVERLAY); + if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) { int rv = umount2(mntpt, flags); diff --git a/sys/contrib/openzfs/lib/libzpool/kernel.c b/sys/contrib/openzfs/lib/libzpool/kernel.c index 452462a1700b..7c8793185d2c 100644 --- a/sys/contrib/openzfs/lib/libzpool/kernel.c +++ b/sys/contrib/openzfs/lib/libzpool/kernel.c @@ -872,9 +872,9 @@ spa_config_load(void) (void) snprintf(pathname, MAXPATHLEN, "%s", spa_config_path); - err = zfs_file_open(pathname, O_RDONLY, 0, &fp); + err = zfs_file_open(pathname, O_RDONLY, 0, kcred, &fp); if (err) - err = zfs_file_open(ZPOOL_CACHE_BOOT, O_RDONLY, 0, &fp); + err = zfs_file_open(ZPOOL_CACHE_BOOT, O_RDONLY, 0, kcred, &fp); kmem_free(pathname, MAXPATHLEN); @@ -1175,8 +1175,10 @@ zvol_rename_minors(spa_t *spa, const char *oldname, const char *newname, * Returns 0 on success underlying error on failure. */ int -zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) +zfs_file_open(const char *path, int flags, int mode, cred_t *cr, + zfs_file_t **fpp) { + (void) cr; int fd; int dump_fd; int err; diff --git a/sys/contrib/openzfs/lib/libzutil/zutil_device_path.c b/sys/contrib/openzfs/lib/libzutil/zutil_device_path.c index 97a2a998bb62..499772e38eeb 100644 --- a/sys/contrib/openzfs/lib/libzutil/zutil_device_path.c +++ b/sys/contrib/openzfs/lib/libzutil/zutil_device_path.c @@ -134,7 +134,7 @@ zfs_strcmp_shortname(const char *name, const char *cmp_name, int wholedisk) while (dir) { /* Trim trailing directory slashes from ZPOOL_IMPORT_PATH */ if (env) { - while (dir[strlen(dir)-1] == '/') + while ((dir[0] != '\0') && (dir[strlen(dir)-1] == '/')) dir[strlen(dir)-1] = '\0'; } diff --git a/sys/contrib/openzfs/man/man1/zhack.1 b/sys/contrib/openzfs/man/man1/zhack.1 index 63658cf930e9..2970d2b3cb7c 100644 --- a/sys/contrib/openzfs/man/man1/zhack.1 +++ b/sys/contrib/openzfs/man/man1/zhack.1 @@ -23,7 +23,7 @@ .\" .\" lint-ok: WARNING: sections out of conventional order: Sh SYNOPSIS .\" -.Dd May 3, 2023 +.Dd August 3, 2026 .Dt ZHACK 1 .Os . @@ -124,6 +124,47 @@ Example: . .It Xo .Nm zhack +.Cm mmp reclaim +.Ar pool +.Xc +Import a +.Ar pool +whose uberblock claim cannot reach every mirror leg the configuration still +expects to be present, then mark the unreachable leaves offline so that +ordinary imports succeed, and export it again. +.Pp +This exists for one situation: a host has failed together with the mirror legs +attached to it, so the surviving labels still describe those legs as healthy, +and every later import demands writes to them that nobody can make. +.Pp +.Sy This does not make the operation safe on its own . +A live host whose legs are +.Em all +invisible from here cannot be detected by any write and read scheme, and this +command will take the pool from it. +Fence the failed host, or otherwise prove it is powered off, before running +this. +.Pp +The activity check itself is unchanged: every write the reachable legs allow, +the wait, and the re-read all still happen, so a competing host which shares +any leg with this one is still detected and the import is refused. +Only the number of writes the claim demands is lowered, and only for mirror +legs this host cannot open, each of which is then marked offline. +.Pp +The pool is left exported, and imports degraded with those leaves offline. +Bring each one back with +.Xr zpool-online 8 +once the hardware returns. +A leaf holding the only copy of some data cannot be offlined: it is reported +and left online, and imports from other hosts stay refused. +No leaf is marked offline on a pool which does not have +.Sy multihost +set, since no claim runs for it and an ordinary import already succeeds. +Leaves of log, cache, and spare vdevs are not touched, as the claim does not +write to them. +. +.It Xo +.Nm zhack .Cm metaslab leak .Op Fl f .Ar pool @@ -181,8 +222,24 @@ descriptions_obj: .No # Nm zhack Cm feature enable Fl d No 'Predict future disk failures.' Ar tank com.example:clairvoyance .No # Nm zhack Cm feature ref Ar tank com.example:clairvoyance .Ed +.Pp +Recover a pool whose peer host died together with its mirror legs, after +confirming that host is down: +.Bd -literal +.No # Nm zpool Cm import Fl f Ar tank +cannot import 'tank': the multihost claim could not be written to a device +the pool configuration expects to be present. +If the device is permanently gone, recover with 'zhack mmp reclaim'. +.No # Nm zhack Cm mmp reclaim Ar tank +tank: marked absent leaf 5646371977210937898 offline +.No # Nm zpool Cm import Ar tank +.No # Nm zpool Cm online Ar tank Ar /dev/disk/by-id/... +.Ed . .Sh SEE ALSO .Xr ztest 1 , .Xr zpool-features 7 , -.Xr zfs 8 +.Xr zpoolprops 7 , +.Xr zfs 8 , +.Xr zpool-import 8 , +.Xr zpool-online 8 diff --git a/sys/contrib/openzfs/man/man4/zfs.4 b/sys/contrib/openzfs/man/man4/zfs.4 index a2faec4e18c4..4e7b0465cbda 100644 --- a/sys/contrib/openzfs/man/man4/zfs.4 +++ b/sys/contrib/openzfs/man/man4/zfs.4 @@ -2757,9 +2757,9 @@ for each I/O submitter. When unset, requests are handled asynchronously by a thread pool. The number of requests which can be handled concurrently is controlled by .Sy zvol_threads . -.Sy zvol_request_sync -is ignored when running on a kernel that supports block multiqueue -.Pq Li blk-mq . +This parameter applies regardless of the +.Sy zvol_use_blk_mq +setting. . .It Sy zvol_num_taskqs Ns = Ns Sy 0 Pq uint Number of zvol taskqs. diff --git a/sys/contrib/openzfs/man/man8/zfs-allow.8 b/sys/contrib/openzfs/man/man8/zfs-allow.8 index e3b0e1ab3e12..76d7ddde660d 100644 --- a/sys/contrib/openzfs/man/man8/zfs-allow.8 +++ b/sys/contrib/openzfs/man/man8/zfs-allow.8 @@ -29,8 +29,9 @@ .\" Copyright 2019 Richard Laager. All rights reserved. .\" Copyright 2018 Nexenta Systems, Inc. .\" Copyright 2019 Joyent, Inc. +.\" Copyright (c) 2026, TrueNAS. .\" -.Dd September 8, 2025 +.Dd June 14, 2026 .Dt ZFS-ALLOW 8 .Os . @@ -214,6 +215,7 @@ rename subcommand Must also have the \fBmount\fR and \fBcreate\fR ability in the rollback subcommand Must also have the \fBmount\fR ability send subcommand Allows sending a replication stream of a dataset. send:raw subcommand Only allows sending raw replication streams, preventing encrypted datasets being sent in decrypted form. +send:encrypted subcommand Only allows sending raw replication streams of encrypted datasets, preventing encrypted datasets being sent in decrypted form and preventing decrypted datasets being sent at all. share subcommand Allows sharing file systems over NFS or SMB protocols snapshot subcommand Must also have the \fBmount\fR ability diff --git a/sys/contrib/openzfs/module/Makefile.in b/sys/contrib/openzfs/module/Makefile.in index 2a3875113110..9dfa221af27e 100644 --- a/sys/contrib/openzfs/module/Makefile.in +++ b/sys/contrib/openzfs/module/Makefile.in @@ -166,7 +166,8 @@ cppcheck-FreeBSD: cppcheck: cppcheck-@ac_system@ distdir: - cd @srcdir@ && find . -name '*.[chS]' -exec sh -c 'for f; do mkdir -p $$distdir/$${f%/*}; cp @srcdir@/$$f $$distdir/$$f; done' _ {} + + ( cd @srcdir@ && find . -name '*.[chS]' ) | \ + while read f; do mkdir -p $$distdir/$${f%/*}; cp @srcdir@/$$f $$distdir/$$f; done cp @srcdir@/Makefile.bsd $$distdir/Makefile.bsd gen-zstd-symbols: diff --git a/sys/contrib/openzfs/module/nvpair/nvpair.c b/sys/contrib/openzfs/module/nvpair/nvpair.c index 52678bb2badb..c990d2d73ad8 100644 --- a/sys/contrib/openzfs/module/nvpair/nvpair.c +++ b/sys/contrib/openzfs/module/nvpair/nvpair.c @@ -1111,6 +1111,21 @@ i_get_value_size(data_type_t type, const void *data, uint_t nelem, uint_t i; size_t newsize; + /* + * Packed STRING_ARRAY values start with a pointer table + * (nelem * sizeof (uint64_t)), with the strings + * following. We need to skip the pointer table for + * the strnlen() bounds check to function as intended. + * We distinguish packed STRING_ARRAY values from + * arbitrary ones using the value of max_size. + */ + if (max_size != (size_t)INT32_MAX) { + /* packed value region */ + if (max_size < value_sz) + return (-1); + max_size -= (size_t)value_sz; + } + /* no alignment requirement for strings */ for (i = 0; i < nelem; i++) { if (strs[i] == NULL) @@ -2980,7 +2995,7 @@ nvpair_native_embedded_array(nvstream_t *nvs, nvpair_t *nvp) return (nvs_embedded_nvl_array(nvs, nvp, NULL)); } -static void +static int nvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp) { switch (nvs->nvs_op) { @@ -2999,15 +3014,32 @@ nvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp) case NVS_OP_DECODE: { char **strp = (void *)NVP_VALUE(nvp); char *buf = ((char *)strp + NVP_NELEM(nvp) * sizeof (uint64_t)); + char *end = (char *)nvp + nvp->nvp_size; int i; for (i = 0; i < NVP_NELEM(nvp); i++) { + size_t max, len; + + if (buf >= end) + return (EFAULT); + + max = (size_t)(end - buf); + len = strnlen(buf, max); + + /* + * The nvpair is corrupt if any of the strings are + * unterminated. + */ + if (len == max) + return (EFAULT); + strp[i] = buf; - buf += strlen(buf) + 1; + buf += len + 1; } break; } } + return (0); } static int @@ -3058,7 +3090,7 @@ nvs_native_nvp_op(nvstream_t *nvs, nvpair_t *nvp) ret = nvpair_native_embedded_array(nvs, nvp); break; case DATA_TYPE_STRING_ARRAY: - nvpair_native_string_array(nvs, nvp); + ret = nvpair_native_string_array(nvs, nvp); break; default: break; @@ -3358,7 +3390,7 @@ nvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp) * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY * is the size of the string(s) excluded. */ - if ((value_sz = i_get_value_size(type, NULL, nelem, NVP_SIZE(nvp)) < 0)) + if ((value_sz = i_get_value_size(type, NULL, nelem, NVP_SIZE(nvp))) < 0) return (EFAULT); /* if there is no data to extract then return */ diff --git a/sys/contrib/openzfs/module/os/freebsd/spl/spl_policy.c b/sys/contrib/openzfs/module/os/freebsd/spl/spl_policy.c index 7fc93648c71e..dfcc92fa7dce 100644 --- a/sys/contrib/openzfs/module/os/freebsd/spl/spl_policy.c +++ b/sys/contrib/openzfs/module/os/freebsd/spl/spl_policy.c @@ -39,13 +39,6 @@ int -secpolicy_nfs(cred_t *cr) -{ - - return (priv_check_cred(cr, PRIV_NFS_DAEMON)); -} - -int secpolicy_zfs(cred_t *cr) { diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/vdev_geom.c b/sys/contrib/openzfs/module/os/freebsd/zfs/vdev_geom.c index bbd1dafc69be..560a7d3ba51f 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/vdev_geom.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/vdev_geom.c @@ -803,8 +803,9 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid) static int vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { + (void) cr; struct g_provider *pp; struct g_consumer *cp; int error, has_trim; diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c index ca13569a1235..11427186575f 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_file_os.c @@ -48,7 +48,8 @@ #include <sys/stat.h> int -zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) +zfs_file_open(const char *path, int flags, int mode, cred_t *cr, + zfs_file_t **fpp) { struct thread *td; struct vnode *vp; @@ -76,7 +77,7 @@ zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) #else NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td); #endif - error = vn_open(&nd, &flags, mode, fp); + error = vn_open_cred(&nd, &flags, mode, 0, cr, fp); if (error != 0) { falloc_abort(td, fp); return (SET_ERROR(error)); @@ -94,7 +95,7 @@ zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) } if (flags & O_TRUNC) { - error = fo_truncate(fp, 0, td->td_ucred, td); + error = fo_truncate(fp, 0, cr, td); if (error != 0) { zfs_file_close(fp); return (SET_ERROR(error)); diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c index 79b784288911..88cd6981f7e4 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c @@ -51,7 +51,7 @@ #include <sys/dsl_prop.h> #include <sys/dsl_dataset.h> #include <sys/dsl_deleg.h> -#include <sys/spa.h> +#include <sys/spa_impl.h> #include <sys/zap.h> #include <sys/sa.h> #include <sys/sa_impl.h> @@ -69,6 +69,7 @@ #include <sys/zfs_quota.h> #include "zfs_comutil.h" +#include "zfs_crrd.h" #ifndef MNTK_VMSETSIZE_BUG #define MNTK_VMSETSIZE_BUG 0 @@ -1038,6 +1039,32 @@ zfsvfs_create(const char *osname, boolean_t readonly, zfsvfs_t **zfvp) return (error); } +int +zfsvfs_create_hold(const char *osname, zfsvfs_t **zfvp) +{ + objset_t *os; + zfsvfs_t *zfsvfs; + int error; + + zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); + + error = dmu_objset_hold(osname, zfsvfs, &os); + if (error != 0) { + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + return (error); + } + + if (dmu_objset_type(os) != DMU_OST_ZFS) { + dmu_objset_rele(os, zfsvfs); + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + return (EINVAL); + } + + zfsvfs->z_use_hold = B_TRUE; + error = zfsvfs_create_impl(zfvp, zfsvfs, os); + + return (error); +} int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os) @@ -1061,7 +1088,10 @@ zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os) error = zfsvfs_init(zfsvfs, os); if (error != 0) { - dmu_objset_disown(os, B_TRUE, zfsvfs); + if (zfsvfs->z_use_hold) + dmu_objset_rele(os, zfsvfs); + else + dmu_objset_disown(os, B_TRUE, zfsvfs); *zfvp = NULL; kmem_free(zfsvfs, sizeof (zfsvfs_t)); return (error); @@ -1315,7 +1345,12 @@ out: dmu_objset_disown(zfsvfs->z_os, B_TRUE, zfsvfs); zfsvfs_free(zfsvfs); } else { + spa_t *spa = zfsvfs->z_os->os_spa; + atomic_inc_32(&zfs_active_fs_count); + + vfsp->mnt_time = dbrrd_latest_time(&spa->spa_txg_log_time); + vfsp->mnt_time = MAX(vfsp->mnt_time, spa->spa_load_txg_ts); } return (error); diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c index 4dd9ed88d433..81dbf3bcf29b 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c @@ -5770,7 +5770,8 @@ zfs_freebsd_pathconf(struct vop_pathconf_args *ap) { ulong_t val; int error; -#if defined(_PC_CLONE_BLKSIZE) || defined(_PC_CASE_INSENSITIVE) +#if defined(_PC_CLONE_BLKSIZE) || defined(_PC_CASE_INSENSITIVE) || \ + defined(_PC_HAS_HIDDENSYSTEM) zfsvfs_t *zfsvfs; #endif @@ -5816,7 +5817,11 @@ zfs_freebsd_pathconf(struct vop_pathconf_args *ap) #endif #ifdef _PC_HAS_HIDDENSYSTEM case _PC_HAS_HIDDENSYSTEM: - *ap->a_retval = 1; + zfsvfs = (zfsvfs_t *)ap->a_vp->v_mount->mnt_data; + if (zfsvfs->z_use_fuids == B_TRUE) + *ap->a_retval = 1; + else + *ap->a_retval = 0; return (0); #endif #ifdef _PC_CLONE_BLKSIZE diff --git a/sys/contrib/openzfs/module/os/linux/zfs/abd_os.c b/sys/contrib/openzfs/module/os/linux/zfs/abd_os.c index 2fd3c3f69165..8ecdb25bc4c5 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/abd_os.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/abd_os.c @@ -1210,154 +1210,6 @@ abd_iter_page(struct abd_iter *aiter) aiter->iter_page_dsize = dsize; } -/* - * Note: ABD BIO functions only needed to support vdev_classic. See comments in - * vdev_disk.c. - */ - -/* - * bio_nr_pages for ABD. - * @off is the offset in @abd - */ -unsigned long -abd_nr_pages_off(abd_t *abd, unsigned int size, size_t off) -{ - unsigned long pos; - - if (abd_is_gang(abd)) { - unsigned long count = 0; - - for (abd_t *cabd = abd_gang_get_offset(abd, &off); - cabd != NULL && size != 0; - cabd = list_next(&ABD_GANG(abd).abd_gang_chain, cabd)) { - ASSERT3U(off, <, cabd->abd_size); - int mysize = MIN(size, cabd->abd_size - off); - count += abd_nr_pages_off(cabd, mysize, off); - size -= mysize; - off = 0; - } - return (count); - } - - if (abd_is_linear(abd)) - pos = (unsigned long)abd_to_buf(abd) + off; - else - pos = ABD_SCATTER(abd).abd_offset + off; - - return (((pos + size + PAGESIZE - 1) >> PAGE_SHIFT) - - (pos >> PAGE_SHIFT)); -} - -static unsigned int -bio_map(struct bio *bio, void *buf_ptr, unsigned int bio_size) -{ - unsigned int offset, size, i; - struct page *page; - - offset = offset_in_page(buf_ptr); - for (i = 0; i < bio->bi_max_vecs; i++) { - size = PAGE_SIZE - offset; - - if (bio_size <= 0) - break; - - if (size > bio_size) - size = bio_size; - - if (is_vmalloc_addr(buf_ptr)) - page = vmalloc_to_page(buf_ptr); - else - page = virt_to_page(buf_ptr); - - /* - * Some network related block device uses tcp_sendpage, which - * doesn't behave well when using 0-count page, this is a - * safety net to catch them. - */ - ASSERT3S(page_count(page), >, 0); - - if (bio_add_page(bio, page, size, offset) != size) - break; - - buf_ptr += size; - bio_size -= size; - offset = 0; - } - - return (bio_size); -} - -/* - * bio_map for gang ABD. - */ -static unsigned int -abd_gang_bio_map_off(struct bio *bio, abd_t *abd, - unsigned int io_size, size_t off) -{ - ASSERT(abd_is_gang(abd)); - - for (abd_t *cabd = abd_gang_get_offset(abd, &off); - cabd != NULL; - cabd = list_next(&ABD_GANG(abd).abd_gang_chain, cabd)) { - ASSERT3U(off, <, cabd->abd_size); - int size = MIN(io_size, cabd->abd_size - off); - int remainder = abd_bio_map_off(bio, cabd, size, off); - io_size -= (size - remainder); - if (io_size == 0 || remainder > 0) - return (io_size); - off = 0; - } - ASSERT0(io_size); - return (io_size); -} - -/* - * bio_map for ABD. - * @off is the offset in @abd - * Remaining IO size is returned - */ -unsigned int -abd_bio_map_off(struct bio *bio, abd_t *abd, - unsigned int io_size, size_t off) -{ - struct abd_iter aiter; - - ASSERT3U(io_size, <=, abd->abd_size - off); - if (abd_is_linear(abd)) - return (bio_map(bio, ((char *)abd_to_buf(abd)) + off, io_size)); - - ASSERT(!abd_is_linear(abd)); - if (abd_is_gang(abd)) - return (abd_gang_bio_map_off(bio, abd, io_size, off)); - - abd_iter_init(&aiter, abd); - abd_iter_advance(&aiter, off); - - for (int i = 0; i < bio->bi_max_vecs; i++) { - struct page *pg; - size_t len, sgoff, pgoff; - struct scatterlist *sg; - - if (io_size <= 0) - break; - - sg = aiter.iter_sg; - sgoff = aiter.iter_offset; - pgoff = sgoff & (PAGESIZE - 1); - len = MIN(io_size, PAGESIZE - pgoff); - ASSERT(len > 0); - - pg = nth_page(sg_page(sg), sgoff >> PAGE_SHIFT); - if (bio_add_page(bio, pg, len, pgoff) != len) - break; - - io_size -= len; - abd_iter_advance(&aiter, len); - } - - return (io_size); -} - EXPORT_SYMBOL(abd_alloc_from_pages); /* Tunable Parameters */ diff --git a/sys/contrib/openzfs/module/os/linux/zfs/policy.c b/sys/contrib/openzfs/module/os/linux/zfs/policy.c index 4396a5d9e076..d3da1a158cdf 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/policy.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/policy.c @@ -25,7 +25,10 @@ * Copyright 2013, Joyent, Inc. All rights reserved. * Copyright (C) 2016 Lawrence Livermore National Security, LLC. * Copyright (c) 2025, Rob Norris <robn@despairlabs.com> - * + * Copyright (c) 2026, TrueNAS. + */ + +/* * For Linux the vast majority of this enforcement is already handled via * the standard Linux VFS permission checks. However certain administrative * commands which bypass the standard mechanisms may need to make use of @@ -87,21 +90,14 @@ priv_policy_user(const cred_t *cr, int capability, int err) } /* - * Checks for operations that are either client-only or are used by - * both clients and servers. - */ -int -secpolicy_nfs(const cred_t *cr) -{ - return (priv_policy(cr, CAP_SYS_ADMIN, EPERM)); -} - -/* * Catch all system configuration. */ int secpolicy_sys_config(const cred_t *cr, boolean_t checkonly) { + /* pools can only be manipulated from the global zone */ + if (crgetzoneid(cr) != GLOBAL_ZONEID) + return (EACCES); return (priv_policy(cr, CAP_SYS_ADMIN, EPERM)); } @@ -241,6 +237,9 @@ secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zidmap_t *mnt_ns, int secpolicy_zinject(const cred_t *cr) { + /* zinject is only in the global zone */ + if (crgetzoneid(cr) != GLOBAL_ZONEID) + return (EACCES); return (priv_policy(cr, CAP_SYS_ADMIN, EACCES)); } @@ -251,6 +250,14 @@ secpolicy_zinject(const cred_t *cr) int secpolicy_zfs(const cred_t *cr) { + /* + * Note that CAP_SYS_ADMIN is effectively "root"-like privileges in + * the given user namespace. Those happen to include mount control + * (matching PRIV_SYS_MOUNT on illumos). There is not a narrower + * permission available. It's important that callers do narrower + * permission checks (eg zone checks) along with this call. + * -- robn, 2026-08-14 + */ return (priv_policy(cr, CAP_SYS_ADMIN, EACCES)); } diff --git a/sys/contrib/openzfs/module/os/linux/zfs/vdev_disk.c b/sys/contrib/openzfs/module/os/linux/zfs/vdev_disk.c index ab7457b04e2e..b00e74bfc029 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/vdev_disk.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/vdev_disk.c @@ -26,6 +26,7 @@ * LLNL-CODE-403049. * Copyright (c) 2012, 2019 by Delphix. All rights reserved. * Copyright (c) 2023, 2024, 2025, Klara, Inc. + * Copyright (c) 2026, TrueNAS. */ #include <sys/zfs_context.h> @@ -281,13 +282,28 @@ vdev_blkdev_put(zfs_bdev_handle_t *bdh, spa_mode_t smode, void *holder) } static int +vdev_path_backing_permission(struct path *path, int mask) +{ +#if defined(HAVE_IDMAP_MNTIDMAP) + return (inode_permission(mnt_idmap(path->mnt), + d_backing_inode(path->dentry), mask)); +#elif defined(HAVE_IDMAP_USERNS) + return (inode_permission(mnt_user_ns(path->mnt), + d_backing_inode(path->dentry), mask)); +#else + return (inode_permission(d_backing_inode(path->dentry), mask)); +#endif +} + +static int vdev_disk_open(vdev_t *v, uint64_t *psize, uint64_t *max_psize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { zfs_bdev_handle_t *bdh; spa_mode_t smode = spa_mode(v->vdev_spa); hrtime_t timeout = MSEC2NSEC(zfs_vdev_open_timeout_ms); vdev_disk_t *vd; + const cred_t *oldcr = NULL; /* Must have a pathname and it must be absolute. */ if (v->vdev_path == NULL || v->vdev_path[0] != '/') { @@ -309,6 +325,13 @@ vdev_disk_open(vdev_t *v, uint64_t *psize, uint64_t *max_psize, char disk_name[BDEVNAME_SIZE + 6] = "/dev/"; boolean_t reread_part = B_FALSE; + /* + * Reopening an already-open device, so the caller credential + * is irrelevant - we had access to it before, we can assume + * we still do. + */ + oldcr = override_creds(kcred); + rw_enter(&vd->vd_lock, RW_WRITER); bdh = vd->vd_bdh; vd->vd_bdh = NULL; @@ -357,6 +380,9 @@ vdev_disk_open(vdev_t *v, uint64_t *psize, uint64_t *max_psize, rw_init(&vd->vd_lock, NULL, RW_DEFAULT, NULL); rw_enter(&vd->vd_lock, RW_WRITER); + + /* Restrict device access below to caller's permissions. */ + oldcr = override_creds(cr); } /* @@ -387,12 +413,37 @@ vdev_disk_open(vdev_t *v, uint64_t *psize, uint64_t *max_psize, * logic in zvol_open(). Extend the timeout and retry the open * subsequent attempts are expected to eventually succeed. */ + hrtime_t start = gethrtime(); - bdh = BDH_ERR_PTR(-ENXIO); - while (BDH_IS_ERR(bdh) && ((gethrtime() - start) < timeout)) { - bdh = vdev_blkdev_get_by_path(v->vdev_path, smode, - zfs_vdev_holder); - if (unlikely(BDH_PTR_ERR(bdh) == -ENOENT)) { + int err = ENXIO; + while (err != 0 && ((gethrtime() - start) < timeout)) { + + /* + * Ensure the caller credential (made live by override_creds() + * above) has access to the device node. This will include + * checking file permissions and considering the + * CAP_DAC_OVERRIDE capability. + */ + struct path devpath; + err = kern_path(v->vdev_path, LOOKUP_FOLLOW, &devpath); + if (err == 0) { + int mask = + (smode & SPA_MODE_READ ? MAY_READ : 0) | + (smode & SPA_MODE_WRITE ? MAY_WRITE : 0); + err = vdev_path_backing_permission(&devpath, mask); + path_put(&devpath); + } + + if (err == 0) { + bdh = vdev_blkdev_get_by_path(v->vdev_path, smode, + zfs_vdev_holder); + err = BDH_IS_ERR(bdh) ? BDH_PTR_ERR(bdh) : 0; + } + + if (err == 0) + break; + + if (unlikely(err == -ENOENT)) { /* * There is no point of waiting since device is removed * explicitly @@ -401,29 +452,30 @@ vdev_disk_open(vdev_t *v, uint64_t *psize, uint64_t *max_psize, break; schedule_timeout_interruptible(MSEC_TO_TICK(10)); - } else if (unlikely(BDH_PTR_ERR(bdh) == -ERESTARTSYS)) { + } else if (unlikely(err == -ERESTARTSYS)) { timeout = MSEC2NSEC(zfs_vdev_open_timeout_ms * 10); continue; - } else if (BDH_IS_ERR(bdh)) { - break; } + + break; } - if (BDH_IS_ERR(bdh)) { - int error = -BDH_PTR_ERR(bdh); - vdev_dbgmsg(v, "open error=%d timeout=%llu/%llu", error, + revert_creds(oldcr); + + if (err != 0) { + vdev_dbgmsg(v, "open error=%d timeout=%llu/%llu", -err, (u_longlong_t)(gethrtime() - start), (u_longlong_t)timeout); vd->vd_bdh = NULL; v->vdev_tsd = vd; rw_exit(&vd->vd_lock); - return (SET_ERROR(error)); - } else { - vd->vd_bdh = bdh; - v->vdev_tsd = vd; - rw_exit(&vd->vd_lock); + return (SET_ERROR(-err)); } + vd->vd_bdh = bdh; + v->vdev_tsd = vd; + rw_exit(&vd->vd_lock); + struct block_device *bdev = BDH_BDEV(vd->vd_bdh); /* Determine the physical block size */ diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zfs_file_os.c b/sys/contrib/openzfs/module/os/linux/zfs/zfs_file_os.c index 3fdcdbac6f68..c4d09022dcf8 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zfs_file_os.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zfs_file_os.c @@ -41,7 +41,8 @@ * Returns 0 on success underlying error on failure. */ int -zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) +zfs_file_open(const char *path, int flags, int mode, cred_t *cr, + zfs_file_t **fpp) { struct file *filp; int saved_umask; @@ -52,7 +53,9 @@ zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) if (flags & O_CREAT) saved_umask = xchg(¤t->fs->umask, 0); + const cred_t *oldcr = override_creds(cr); filp = filp_open(path, flags, mode); + revert_creds(oldcr); if (flags & O_CREAT) (void) xchg(¤t->fs->umask, saved_umask); diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zfs_vfsops.c b/sys/contrib/openzfs/module/os/linux/zfs/zfs_vfsops.c index 2f393cd12e34..2e2e65c82c15 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zfs_vfsops.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zfs_vfsops.c @@ -622,6 +622,32 @@ zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os) } int +zfsvfs_create_hold(const char *osname, zfsvfs_t **zfvp) +{ + objset_t *os; + zfsvfs_t *zfsvfs; + int error; + + zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); + + error = dmu_objset_hold(osname, zfsvfs, &os); + if (error != 0) { + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + return (error); + } + + if (dmu_objset_type(os) != DMU_OST_ZFS) { + dmu_objset_rele(os, zfsvfs); + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + return (EINVAL); + } + + zfsvfs->z_use_hold = B_TRUE; + error = zfsvfs_create_impl(zfvp, zfsvfs, os); + return (error); +} + +int zfsvfs_create(const char *osname, boolean_t readonly, zfsvfs_t **zfvp) { objset_t *os; @@ -678,7 +704,10 @@ zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os) error = zfsvfs_init(zfsvfs, os); if (error != 0) { - dmu_objset_disown(os, B_TRUE, zfsvfs); + if (zfsvfs->z_use_hold) + dmu_objset_rele(os, zfsvfs); + else + dmu_objset_disown(os, B_TRUE, zfsvfs); *zfvp = NULL; zfsvfs_free(zfsvfs); return (error); diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zfs_vnops_os.c b/sys/contrib/openzfs/module/os/linux/zfs/zfs_vnops_os.c index d6dad70ae09f..cb6321e4f454 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zfs_vnops_os.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zfs_vnops_os.c @@ -284,6 +284,13 @@ update_pages(znode_t *zp, int64_t start, int len, objset_t *os) * When a file is memory mapped, we must keep the I/O data synchronized * between the DMU cache and the memory mapped pages. Preferentially read * from memory mapped pages, otherwise fallback to reading through the dmu. + * + * A run of non-resident pages is read from the DMU in a single call rather + * than one call per page. A page may become resident between the lookup + * and the read, but that is safe: zfs_read() holds the rangelock as reader, + * so the DMU contents of the range are stable (writes, writeback and + * truncation take the writer lock) and a concurrently faulted page is + * filled by zfs_getpage() from those same contents. */ int mappedread(znode_t *zp, int nbytes, zfs_uio_t *uio) @@ -329,6 +336,20 @@ mappedread(znode_t *zp, int nbytes, zfs_uio_t *uio) mark_page_accessed(pp); put_page(pp); } else { + /* + * Extend the read over any following non-resident + * pages so they are fetched in one DMU call. + */ + while (bytes < len) { + struct page *tp = find_get_page(mp, + (start + PAGE_SIZE) >> PAGE_SHIFT); + if (tp != NULL) { + put_page(tp); + break; + } + bytes += MIN(PAGE_SIZE, len - bytes); + start += PAGE_SIZE; + } error = dmu_read_uio_dbuf(sa_get_db(zp->z_sa_hdl), uio, bytes, DMU_READ_PREFETCH); } @@ -4149,7 +4170,18 @@ zfs_fillpage(struct inode *ip, struct page *pp) u_offset_t io_off = page_offset(pp); size_t io_len = PAGE_SIZE; - ASSERT3U(io_off, <, i_size); + /* + * The page may be faulted in after the file has been truncated. + * There is no data to read; just zero-fill the page. + */ + if (io_off >= i_size) { + void *zva = kmap(pp); + memset(zva, 0, PAGE_SIZE); + kunmap(pp); + ClearPageError(pp); + SetPageUptodate(pp); + return (0); + } if (io_off + io_len > i_size) io_len = i_size - io_off; @@ -4200,9 +4232,14 @@ zfs_getpage(struct inode *ip, struct page *pp) if ((error = zfs_enter_verify_zp(zfsvfs, zp, FTAG)) != 0) return (error); - ASSERT3U(io_off, <, i_size); - - if (io_off + io_len > i_size) + /* + * If the page lies entirely at or beyond EOF (e.g. it raced a + * truncate) just lock the page and let zfs_fillpage() re-check + * i_size under the range lock and zero-fill it. + */ + if (io_off >= i_size) + io_len = PAGE_SIZE; + else if (io_off + io_len > i_size) io_len = i_size - io_off; /* diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zfs_znode_os.c b/sys/contrib/openzfs/module/os/linux/zfs/zfs_znode_os.c index 03976d01a0e3..dd6bd21a6042 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zfs_znode_os.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zfs_znode_os.c @@ -60,6 +60,9 @@ #include <sys/zfs_sa.h> #include <sys/zfs_stat.h> #include <linux/mm_compat.h> +#ifdef CONFIG_FS_POSIX_ACL +#include <linux/posix_acl.h> +#endif #include "zfs_prop.h" #include "zfs_comutil.h" @@ -1194,6 +1197,12 @@ zfs_rezget(znode_t *zp) } mutex_exit(&zp->z_acl_lock); +#ifdef CONFIG_FS_POSIX_ACL + /* The VFS cache can still describe the pre-rollback inode. */ + forget_cached_acl(ZTOI(zp), ACL_TYPE_ACCESS); + forget_cached_acl(ZTOI(zp), ACL_TYPE_DEFAULT); +#endif + rw_enter(&zp->z_xattr_lock, RW_WRITER); if (zp->z_xattr_cached) { nvlist_free(zp->z_xattr_cached); diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zpl_ctldir.c b/sys/contrib/openzfs/module/os/linux/zfs/zpl_ctldir.c index 81ac26cb0c93..453628ab4079 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zpl_ctldir.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zpl_ctldir.c @@ -185,10 +185,10 @@ zpl_snapdir_automount(struct path *path) } /* - * Negative dentries must always be revalidated so newly created snapshots - * can be detected and automounted. Normal dentries should be kept because - * as of the 3.18 kernel revaliding the mountpoint dentry will result in - * the snapshot being immediately unmounted. + * Negative dentries must always be revalidated so newly created snapshots can + * be detected and automounted. Normal dentries should be kept because + * revalidating the mountpoint dentry will result in the snapshot being + * immediately unmounted. */ #ifdef HAVE_D_REVALIDATE_4ARGS static int @@ -203,14 +203,6 @@ zpl_snapdir_revalidate(struct dentry *dentry, unsigned int flags) } static const struct dentry_operations zpl_dops_snapdirs = { -/* - * Auto mounting of snapshots is only supported for 2.6.37 and - * newer kernels. Prior to this kernel the ops->follow_link() - * callback was used as a hack to trigger the mount. The - * resulting vfsmount was then explicitly grafted in to the - * name space. While it might be possible to add compatibility - * code to accomplish this it would require considerable care. - */ .d_automount = zpl_snapdir_automount, .d_revalidate = zpl_snapdir_revalidate, }; @@ -330,15 +322,15 @@ out: static int #ifdef HAVE_IOPS_RENAME_USERNS -zpl_snapdir_rename2(struct user_namespace *user_ns, struct inode *sdip, +zpl_snapdir_rename(struct user_namespace *user_ns, struct inode *sdip, struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, unsigned int flags) #elif defined(HAVE_IOPS_RENAME_IDMAP) -zpl_snapdir_rename2(struct mnt_idmap *user_ns, struct inode *sdip, +zpl_snapdir_rename(struct mnt_idmap *user_ns, struct inode *sdip, struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, unsigned int flags) #else -zpl_snapdir_rename2(struct inode *sdip, struct dentry *sdentry, +zpl_snapdir_rename(struct inode *sdip, struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, unsigned int flags) #endif { @@ -358,17 +350,6 @@ zpl_snapdir_rename2(struct inode *sdip, struct dentry *sdentry, return (error); } -#if (!defined(HAVE_RENAME_WANTS_FLAGS) && \ - !defined(HAVE_IOPS_RENAME_USERNS) && \ - !defined(HAVE_IOPS_RENAME_IDMAP)) -static int -zpl_snapdir_rename(struct inode *sdip, struct dentry *sdentry, - struct inode *tdip, struct dentry *tdentry) -{ - return (zpl_snapdir_rename2(sdip, sdentry, tdip, tdentry, 0)); -} -#endif - static int zpl_snapdir_rmdir(struct inode *dip, struct dentry *dentry) { @@ -512,13 +493,7 @@ const struct file_operations zpl_fops_snapdir = { const struct inode_operations zpl_ops_snapdir = { .lookup = zpl_snapdir_lookup, .getattr = zpl_snapdir_getattr, -#if (defined(HAVE_RENAME_WANTS_FLAGS) || \ - defined(HAVE_IOPS_RENAME_USERNS) || \ - defined(HAVE_IOPS_RENAME_IDMAP)) - .rename = zpl_snapdir_rename2, -#else .rename = zpl_snapdir_rename, -#endif .rmdir = zpl_snapdir_rmdir, .mkdir = zpl_snapdir_mkdir, }; diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zpl_file.c b/sys/contrib/openzfs/module/os/linux/zfs/zpl_file.c index 6d57bff56540..a971aabf2a62 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zpl_file.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zpl_file.c @@ -741,7 +741,12 @@ zpl_fallocate_common(struct inode *ip, int mode, loff_t offset, loff_t len) if (error) goto out_unmark; - error = -zfs_freesp(zp, offset + len, 0, 0, FALSE); + /* + * extend file: log=TRUE drives z_seq bump, + * mtime/ctime advance, and TX_TRUNCATE ZIL + * record; matches zfs_space(). + */ + error = -zfs_freesp(zp, offset + len, 0, 0, TRUE); zfs_exit(zfsvfs, FTAG); } } diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zpl_inode.c b/sys/contrib/openzfs/module/os/linux/zfs/zpl_inode.c index e4e15c824f4b..3492f4a033e9 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zpl_inode.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zpl_inode.c @@ -643,15 +643,15 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia) static int #ifdef HAVE_IOPS_RENAME_USERNS -zpl_rename2(struct user_namespace *user_ns, struct inode *sdip, +zpl_rename(struct user_namespace *user_ns, struct inode *sdip, struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, unsigned int rflags) #elif defined(HAVE_IOPS_RENAME_IDMAP) -zpl_rename2(struct mnt_idmap *user_ns, struct inode *sdip, +zpl_rename(struct mnt_idmap *user_ns, struct inode *sdip, struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, unsigned int rflags) #else -zpl_rename2(struct inode *sdip, struct dentry *sdentry, +zpl_rename(struct inode *sdip, struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, unsigned int rflags) #endif { @@ -686,17 +686,6 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry, return (error); } -#if !defined(HAVE_IOPS_RENAME_USERNS) && \ - !defined(HAVE_RENAME_WANTS_FLAGS) && \ - !defined(HAVE_IOPS_RENAME_IDMAP) -static int -zpl_rename(struct inode *sdip, struct dentry *sdentry, - struct inode *tdip, struct dentry *tdentry) -{ - return (zpl_rename2(sdip, sdentry, tdip, tdentry, 0)); -} -#endif - static int #ifdef HAVE_IOPS_SYMLINK_USERNS zpl_symlink(struct user_namespace *user_ns, struct inode *dir, @@ -861,13 +850,7 @@ const struct inode_operations zpl_dir_inode_operations = { .mkdir = zpl_mkdir, .rmdir = zpl_rmdir, .mknod = zpl_mknod, -#if defined(HAVE_RENAME_WANTS_FLAGS) || defined(HAVE_IOPS_RENAME_USERNS) - .rename = zpl_rename2, -#elif defined(HAVE_IOPS_RENAME_IDMAP) - .rename = zpl_rename2, -#else .rename = zpl_rename, -#endif .tmpfile = zpl_tmpfile, .setattr = zpl_setattr, .getattr = zpl_getattr, diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zpl_super.c b/sys/contrib/openzfs/module/os/linux/zfs/zpl_super.c index d7194e4f1f78..b89226d134df 100644 --- a/sys/contrib/openzfs/module/os/linux/zfs/zpl_super.c +++ b/sys/contrib/openzfs/module/os/linux/zfs/zpl_super.c @@ -342,10 +342,10 @@ zpl_show_options(struct seq_file *seq, struct dentry *root) } static int -zpl_test_super(struct super_block *s, void *data) +zpl_test_super(struct super_block *s, struct fs_context *fc) { zfsvfs_t *zfsvfs = s->s_fs_info; - objset_t *os = data; + objset_t *os = fc->sget_key; /* * If the os doesn't match the z_os in the super_block, assume it is * not a match. Matching would imply a multimount of a dataset. It is @@ -859,8 +859,8 @@ zpl_get_tree(struct fs_context *fc) return (-err); /* - * The dsl pool lock must be released prior to calling sget(). - * It is possible sget() may block on the lock in grab_super() + * The dsl pool lock must be released prior to calling sget_fc(). + * It is possible sget_fc() may block on the lock in grab_super() * while deactivate_super() holds that same lock and waits for * a txg sync. If the dsl_pool lock is held over sget() * this can prevent the pool sync and cause a deadlock. @@ -868,8 +868,9 @@ zpl_get_tree(struct fs_context *fc) dsl_dataset_long_hold(dmu_objset_ds(os), FTAG); dsl_pool_rele(dmu_objset_pool(os), FTAG); - sb = sget(fc->fs_type, zpl_test_super, set_anon_super, - fc->sb_flags, os); + fc->sget_key = os; + sb = sget_fc(fc, zpl_test_super, set_anon_super_fc); + fc->sget_key = NULL; /* * Recheck with the lock held to prevent mounting the wrong dataset diff --git a/sys/contrib/openzfs/module/zcommon/zfs_deleg.c b/sys/contrib/openzfs/module/zcommon/zfs_deleg.c index 87596558c9a1..422c5544974d 100644 --- a/sys/contrib/openzfs/module/zcommon/zfs_deleg.c +++ b/sys/contrib/openzfs/module/zcommon/zfs_deleg.c @@ -60,6 +60,7 @@ const zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = { {ZFS_DELEG_PERM_SHARE}, {ZFS_DELEG_PERM_SEND}, {ZFS_DELEG_PERM_SEND_RAW}, + {ZFS_DELEG_PERM_SEND_ENCRYPTED}, {ZFS_DELEG_PERM_USERPROP}, {ZFS_DELEG_PERM_USERQUOTA}, {ZFS_DELEG_PERM_GROUPQUOTA}, diff --git a/sys/contrib/openzfs/module/zfs/arc.c b/sys/contrib/openzfs/module/zfs/arc.c index 22f9be7fbd2d..34dad8b0e27e 100644 --- a/sys/contrib/openzfs/module/zfs/arc.c +++ b/sys/contrib/openzfs/module/zfs/arc.c @@ -1050,6 +1050,7 @@ buf_hash(uint64_t spa, const dva_t *dva, uint64_t birth) static void buf_discard_identity(arc_buf_hdr_t *hdr) { + VERIFY(!HDR_IN_HASH_TABLE(hdr)); hdr->b_dva.dva_word[0] = 0; hdr->b_dva.dva_word[1] = 0; hdr->b_birth = 0; @@ -1131,7 +1132,7 @@ buf_hash_remove(arc_buf_hdr_t *hdr) arc_buf_hdr_t *fhdr, **hdrp; uint64_t idx = BUF_HASH_INDEX(hdr->b_spa, &hdr->b_dva, hdr->b_birth); - ASSERT(MUTEX_HELD(BUF_HASH_LOCK(idx))); + VERIFY(MUTEX_HELD(BUF_HASH_LOCK(idx))); ASSERT(HDR_IN_HASH_TABLE(hdr)); hdrp = &buf_hash_table.ht_table[idx]; @@ -2463,7 +2464,7 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *hdr) update_new = B_TRUE; ASSERT(MUTEX_HELD(HDR_LOCK(hdr))); - ASSERT3P(new_state, !=, old_state); + VERIFY3P(new_state, !=, old_state); /* * If this buffer is evictable, transfer it from the @@ -3706,7 +3707,7 @@ arc_hdr_destroy(arc_buf_hdr_t *hdr) arc_hdr_free_abd(hdr, B_TRUE); } - ASSERT0P(hdr->b_hash_next); + VERIFY0P(hdr->b_hash_next); if (HDR_HAS_L1HDR(hdr)) { ASSERT(!multilist_link_active(&hdr->b_l1hdr.b_arc_node)); ASSERT0P(hdr->b_l1hdr.b_acb); @@ -6744,7 +6745,7 @@ arc_release(arc_buf_t *buf, const void *tag) ASSERT(zfs_refcount_count(&hdr->b_l1hdr.b_refcnt) == 1); /* protected by hash lock, or hdr is on arc_anon */ ASSERT(!multilist_link_active(&hdr->b_l1hdr.b_arc_node)); - ASSERT(!HDR_IO_IN_PROGRESS(hdr)); + VERIFY(!HDR_IO_IN_PROGRESS(hdr)); if (HDR_HAS_L2HDR(hdr)) { mutex_enter(&hdr->b_l2hdr.b_dev->l2ad_mtx); @@ -7000,13 +7001,13 @@ arc_write_done(zio_t *zio) if (!BP_EQUAL(&zio->io_bp_orig, zio->io_bp)) panic("bad overwrite, hdr=%p exists=%p", (void *)hdr, (void *)exists); - ASSERT(zfs_refcount_is_zero( + VERIFY(zfs_refcount_is_zero( &exists->b_l1hdr.b_refcnt)); arc_change_state(arc_anon, exists); arc_hdr_destroy(exists); mutex_exit(hash_lock); exists = buf_hash_insert(hdr, &hash_lock); - ASSERT0P(exists); + VERIFY0P(exists); } else if (zio->io_flags & ZIO_FLAG_NOPWRITE) { /* nopwrite */ ASSERT(zio->io_prop.zp_nopwrite); @@ -11107,9 +11108,15 @@ l2arc_log_blkptr_valid(l2arc_dev_t *dev, const l2arc_log_blkptr_t *lbp) l2arc_range_check_overlap(dev->l2ad_hand, dev->l2ad_evict, start) || l2arc_range_check_overlap(dev->l2ad_hand, dev->l2ad_evict, end); - return (start >= dev->l2ad_start && end <= dev->l2ad_end && - asize > 0 && asize <= sizeof (l2arc_log_blk_phys_t) && - (!evicted || dev->l2ad_first)); + if (asize == 0 || asize > sizeof (l2arc_log_blk_phys_t) || + start < dev->l2ad_start || end > dev->l2ad_end) + return (B_FALSE); + + /* On a first sweep only the region below the write hand was written. */ + if (dev->l2ad_first) + return (end < dev->l2ad_hand); + + return (!evicted); } /* diff --git a/sys/contrib/openzfs/module/zfs/brt.c b/sys/contrib/openzfs/module/zfs/brt.c index 08a6bd52ab31..63292be10edf 100644 --- a/sys/contrib/openzfs/module/zfs/brt.c +++ b/sys/contrib/openzfs/module/zfs/brt.c @@ -582,8 +582,10 @@ brt_vdev_load(spa_t *spa, brt_vdev_t *brtvd) error = dmu_read(spa->spa_meta_objset, brtvd->bv_mos_brtvdev, 0, MIN(brtvd->bv_size, bvphys->bvp_size) * sizeof (uint16_t), brtvd->bv_entcount, DMU_READ_NO_PREFETCH | DMU_UNCACHEDIO); - if (error != 0) + if (error != 0) { + dmu_buf_rele(db, FTAG); return (error); + } ASSERT(bvphys->bvp_mos_entries != 0); VERIFY0(dnode_hold(spa->spa_meta_objset, bvphys->bvp_mos_entries, brtvd, diff --git a/sys/contrib/openzfs/module/zfs/dbuf.c b/sys/contrib/openzfs/module/zfs/dbuf.c index a4fe3e519700..9e899e3ea334 100644 --- a/sys/contrib/openzfs/module/zfs/dbuf.c +++ b/sys/contrib/openzfs/module/zfs/dbuf.c @@ -1476,17 +1476,25 @@ dbuf_read_hole(dmu_buf_impl_t *db, dnode_t *dn, blkptr_t *bp) int is_hole = bp == NULL || BP_IS_HOLE(bp); /* - * For level 0 blocks only, if the above check fails: - * Recheck BP_IS_HOLE() after dnode_block_freed() in case dnode_sync() - * processes the delete record and clears the bp while we are waiting - * for the dn_mtx (resulting in a "no" from block_freed). + * For level 0 blocks only, if the above check didn't find a hole, + * consult dnode_block_freed() to check for pending frees. * - * If bp != db->db_blkptr, it means that it was overridden (by a block - * clone or direct I/O write). We cannot rely on dnode_block_freed as - * the range can be freed in an earlier TXG but overridden in later. + * If the block has been overridden by a block clone or direct I/O + * write, we can't use dnode_block_freed() directly because it would + * find frees from TXGs before the override, which should not make + * the block appear freed. Instead, check only free ranges from + * TXGs after the override. */ - if (!is_hole && db->db_level == 0 && bp == db->db_blkptr) - is_hole = dnode_block_freed(dn, db->db_blkid) || BP_IS_HOLE(bp); + if (!is_hole && db->db_level == 0) { + dbuf_dirty_record_t *dr = list_head(&db->db_dirty_records); + if (dr != NULL && + (dr->dt.dl.dr_brtwrite || dr->dt.dl.dr_diowrite)) { + is_hole = dnode_block_freed_after(dn, + db->db_blkid, dr->dr_txg); + } else { + is_hole = dnode_block_freed(dn, db->db_blkid); + } + } if (is_hole) { db_data = dbuf_alloc_arcbuf(db); diff --git a/sys/contrib/openzfs/module/zfs/ddt.c b/sys/contrib/openzfs/module/zfs/ddt.c index 945d79d056f7..1deacb649d53 100644 --- a/sys/contrib/openzfs/module/zfs/ddt.c +++ b/sys/contrib/openzfs/module/zfs/ddt.c @@ -2820,8 +2820,8 @@ ddt_prune_walk(spa_t *spa, uint64_t cutoff, ddt_age_histo_t *histogram) }; ddt_lightweight_entry_t ddlwe = {0}; int error; - int valid = 0; - int candidates = 0; + uint64_t valid = 0; + uint64_t candidates = 0; uint64_t now = gethrestime_sec(); ddt_prune_info_t dpi; boolean_t pruning = (cutoff != 0); @@ -2873,8 +2873,9 @@ ddt_prune_walk(spa_t *spa, uint64_t cutoff, ddt_age_histo_t *histogram) /* build a histogram */ if (histogram != NULL) { - uint64_t age = MAX(1, (now - class_start) / 3600); - int bin = MIN(highbit64(age) - 1, HIST_BINS - 1); + uint64_t age = (class_start < now) ? + (now - class_start) / 3600 : 0; + int bin = MIN(highbit64(age), HIST_BINS - 1); histogram->dah_entries++; histogram->dah_age_histo[bin]++; } @@ -2882,7 +2883,7 @@ ddt_prune_walk(spa_t *spa, uint64_t cutoff, ddt_age_histo_t *histogram) valid++; } - if (pruning && valid > 0) { + if (pruning) { if (!list_is_empty(&dpi.dpi_candidates)) { /* sync out final batch of prune candidates */ VERIFY0(dsl_sync_task(spa_name(spa), NULL, @@ -2891,10 +2892,13 @@ ddt_prune_walk(spa_t *spa, uint64_t cutoff, ddt_age_histo_t *histogram) } list_destroy(&dpi.dpi_candidates); - zfs_dbgmsg("pruned %llu entries (%d%%) across %llu txg syncs", - (u_longlong_t)dpi.dpi_pruned, - (int)((dpi.dpi_pruned * 100) / valid), - (u_longlong_t)dpi.dpi_txg_syncs); + if (valid > 0) { + zfs_dbgmsg("pruned %llu entries (%llu%%) across " + "%llu txg syncs", + (u_longlong_t)dpi.dpi_pruned, + (u_longlong_t)((dpi.dpi_pruned * 100) / valid), + (u_longlong_t)dpi.dpi_txg_syncs); + } } } @@ -2924,6 +2928,7 @@ ddt_prune_unique_entries(spa_t *spa, zpool_ddt_prune_unit_t unit, zfs_dbgmsg("prune %llu %s", (u_longlong_t)amount, unit == ZPOOL_DDT_PRUNE_PERCENTAGE ? "%" : "seconds old or older"); + uint64_t now = gethrestime_sec(); if (unit == ZPOOL_DDT_PRUNE_PERCENTAGE) { ddt_age_histo_t histogram; uint64_t oldest = 0; @@ -2931,7 +2936,7 @@ ddt_prune_unique_entries(spa_t *spa, zpool_ddt_prune_unit_t unit, /* Make a pass over DDT to build a histogram */ ddt_prune_walk(spa, 0, &histogram); - int target = (histogram.dah_entries * amount) / 100; + uint64_t target = (histogram.dah_entries * amount) / 100; /* * Figure out our cutoff date @@ -2939,23 +2944,25 @@ ddt_prune_unique_entries(spa_t *spa, zpool_ddt_prune_unit_t unit, */ for (int i = HIST_BINS - 1; i >= 0 && target > 0; i--) { if (histogram.dah_age_histo[i] != 0) { - /* less than this bucket remaining */ - if (target < histogram.dah_age_histo[i]) { - oldest = MAX(1, (1<<i) * 3600); + if (target <= histogram.dah_age_histo[i]) { + oldest = (i == 0) ? 0 : + (1ULL << (i - 1)) * 3600; target = 0; } else { target -= histogram.dah_age_histo[i]; } } } - cutoff = gethrestime_sec() - oldest; + cutoff = now - oldest; if (ddt_dump_prune_histogram) ddt_dump_age_histogram(&histogram, cutoff); } else if (unit == ZPOOL_DDT_PRUNE_AGE) { - cutoff = gethrestime_sec() - amount; + if (amount >= now) + return (SET_ERROR(EINVAL)); + cutoff = now - amount; } else { - return (EINVAL); + return (SET_ERROR(EINVAL)); } if (cutoff > 0 && !spa_shutting_down(spa) && !issig()) { diff --git a/sys/contrib/openzfs/module/zfs/ddt_log.c b/sys/contrib/openzfs/module/zfs/ddt_log.c index e36c15085baa..9c3efefa2c00 100644 --- a/sys/contrib/openzfs/module/zfs/ddt_log.c +++ b/sys/contrib/openzfs/module/zfs/ddt_log.c @@ -211,7 +211,7 @@ ddt_log_begin(ddt_t *ddt, size_t nentries, dmu_tx_t *tx, ddt_log_update_t *dlu) ASSERT3U(reclen, <=, UINT16_MAX); dlu->dlu_reclen = reclen; - VERIFY0(dnode_hold(ddt->ddt_os, ddt->ddt_log_active->ddl_object, FTAG, + VERIFY0(dnode_hold(ddt->ddt_os, ddt->ddt_log_active->ddl_object, dlu, &dlu->dlu_dn)); dnode_set_storage_type(dlu->dlu_dn, DMU_OT_DDT_ZAP); @@ -342,7 +342,7 @@ ddt_log_commit(ddt_t *ddt, ddt_log_update_t *dlu) ddt->ddt_log_active->ddl_length += dlu->dlu_ndbp * (uint64_t)dlu->dlu_dn->dn_datablksz; - dnode_rele(dlu->dlu_dn, FTAG); + dnode_rele(dlu->dlu_dn, dlu); ddt_log_update_header(ddt, ddt->ddt_log_active, dlu->dlu_tx); diff --git a/sys/contrib/openzfs/module/zfs/dmu_recv.c b/sys/contrib/openzfs/module/zfs/dmu_recv.c index 74874bb65d37..b0503c222320 100644 --- a/sys/contrib/openzfs/module/zfs/dmu_recv.c +++ b/sys/contrib/openzfs/module/zfs/dmu_recv.c @@ -2272,14 +2272,21 @@ flush_write_batch_impl(struct receive_writer_arg *rwa) if (drrw->drr_logical_size != dn->dn_datablksz) { /* - * The WRITE record is larger than the object's block - * size. We must be receiving an incremental - * large-block stream into a dataset that previously did - * a non-large-block receive. Lightweight writes must - * be exactly one block, so we need to decompress the - * data (if compressed) and do a normal dmu_write(). + * The WRITE record size does not match the + * object's block size. This happens when the + * record is larger than the block size (an + * incremental large-block stream received into a + * dataset that previously did a non-large-block + * receive), and also when it is a smaller trailing + * chunk from splitting a large block for a stream + * sent without large blocks: a single-block object + * can have a non-power-of-2 block size, so its final + * SPA_OLD_MAXBLOCKSIZE-sized chunk may be shorter + * than the block size. Either way a lightweight + * write is not possible (those must cover exactly + * one block), so we decompress the data (if + * compressed) and do a normal dmu_write(). */ - ASSERT3U(drrw->drr_logical_size, >, dn->dn_datablksz); if (DRR_WRITE_COMPRESSED(drrw)) { abd_t *decomp_abd = abd_alloc_linear(drrw->drr_logical_size, @@ -2484,7 +2491,9 @@ receive_process_write_record(struct receive_writer_arg *rwa, } struct receive_record_arg *first_rrd = list_head(&rwa->write_batch); - struct drr_write *first_drrw = &first_rrd->header.drr_u.drr_write; + struct drr_write *first_drrw = NULL; + if (first_rrd != NULL) + first_drrw = &first_rrd->header.drr_u.drr_write; uint64_t batch_size = MIN(zfs_recv_write_batch_size, DMU_MAX_ACCESS / 2); if (first_rrd != NULL && diff --git a/sys/contrib/openzfs/module/zfs/dnode.c b/sys/contrib/openzfs/module/zfs/dnode.c index be0e3de9bb23..9c1b615c263c 100644 --- a/sys/contrib/openzfs/module/zfs/dnode.c +++ b/sys/contrib/openzfs/module/zfs/dnode.c @@ -2468,6 +2468,37 @@ dnode_block_freed(dnode_t *dn, uint64_t blkid) return (i < TXG_SIZE); } +/* + * Check if a level-0 block was freed in a TXG after override_txg. + * + * When a block has been overridden (e.g., by block clone or direct I/O), + * we can't use dnode_block_freed() because it checks all active TXGs. + * A free from a TXG *before* the override should not make the block appear + * freed. + */ +uint64_t +dnode_block_freed_after(dnode_t *dn, uint64_t blkid, uint64_t override_txg) +{ + ASSERT(blkid != DMU_BONUS_BLKID); + ASSERT(blkid != DMU_SPILL_BLKID); + + if (dn->dn_free_txg) + return (TRUE); + + mutex_enter(&dn->dn_mtx); + uint64_t open = spa_open_txg(dmu_objset_spa(dn->dn_objset)); + for (uint64_t txg = override_txg + 1; txg <= open; txg++) { + int i = txg & TXG_MASK; + if (dn->dn_free_ranges[i] != NULL && + zfs_range_tree_contains(dn->dn_free_ranges[i], blkid, 1)) { + mutex_exit(&dn->dn_mtx); + return (TRUE); + } + } + mutex_exit(&dn->dn_mtx); + return (FALSE); +} + /* call from syncing context when we actually write/free space for this dnode */ void dnode_diduse_space(dnode_t *dn, int64_t delta) diff --git a/sys/contrib/openzfs/module/zfs/mmp.c b/sys/contrib/openzfs/module/zfs/mmp.c index aedcf9505f8d..f5ef8192a30e 100644 --- a/sys/contrib/openzfs/module/zfs/mmp.c +++ b/sys/contrib/openzfs/module/zfs/mmp.c @@ -196,6 +196,22 @@ uint_t zfs_multihost_import_intervals = MMP_DEFAULT_IMPORT_INTERVALS; */ uint_t zfs_multihost_fail_intervals = MMP_DEFAULT_FAIL_INTERVALS; +#ifndef _KERNEL +/* + * Manual recovery only, set by zhack. Drops the mirror legs this host + * cannot open from the uberblock claim's required write count, so a pool + * whose config still expects legs that died with a peer host can be + * imported by hand. Every leg which can be opened is still required. + * + * This is deliberately absent from the kernel module. The write, the wait + * and the re-read are untouched, so a competing importer which shares any + * visibility with us is still caught; a live peer whose legs are all + * invisible from here is not, and cannot be by any write-and-read scheme. + * That residual is why this is a manual operation guarded by fencing. + */ +boolean_t mmp_claim_relaxed = B_FALSE; +#endif + static const void *const mmp_tag = "mmp_write_uberblock"; static __attribute__((noreturn)) void mmp_thread(void *arg); @@ -560,17 +576,20 @@ mmp_claim_uberblock_sync_done(zio_t *zio) /* * Write the uberblock to the first label of all leaves of the specified vdev. - * Two writes required for each mirror, one for a singleton, and parity+1 for - * raidz or draid vdevs. + * One write per mirror leg the config still expects present, one for a + * singleton, and parity+1 for raidz or draid vdevs. */ static void mmp_claim_uberblock_sync(zio_t *zio, uint64_t *good_writes, - uint64_t *req_writes, uberblock_t *ub, vdev_t *vd, int flags) + uint64_t *issued_writes, uint64_t *req_writes, uberblock_t *ub, vdev_t *vd, + int flags) { for (uint64_t c = 0; c < vd->vdev_children; c++) { vdev_t *cvd = vd->vdev_child[c]; - if (cvd->vdev_islog || cvd->vdev_isspare || cvd->vdev_isl2cache) + if (cvd->vdev_islog || cvd->vdev_isspare || + cvd->vdev_isl2cache || cvd->vdev_ishole || + cvd->vdev_ops == &vdev_indirect_ops) continue; if (cvd->vdev_top == cvd) { @@ -578,13 +597,50 @@ mmp_claim_uberblock_sync(zio_t *zio, uint64_t *good_writes, if (nparity) { *req_writes += nparity + 1; } else { - *req_writes += - MIN(MAX(cvd->vdev_children, 1), 2); + /* + * Mirror: any single leg is enough for a + * remote host to see the claim, so require a + * write to every leg the pool config still + * expects to be present. A leg taken out of + * service is recorded persistently in the + * config (offline, faulted, or removed) and + * is seen the same way by every host, so it + * is not required and a degraded mirror can + * still be claimed. A leg merely unreachable + * from this host has none of those states and + * stays required, so a host which can see + * only some of the legs of an otherwise + * healthy mirror still fails the claim and + * cannot split the pool. + */ + uint64_t present = 0; + for (uint64_t l = 0; l < cvd->vdev_children; + l++) { + vdev_t *lvd = cvd->vdev_child[l]; +#ifndef _KERNEL + /* + * Manual recovery forgives the legs + * this host could not open, and marks + * that same set offline before it + * exports, so the relaxed claim + * accepts nothing the later ordinary + * imports would not. + */ + if (mmp_claim_relaxed && + lvd->vdev_not_present) + continue; +#endif + if (!lvd->vdev_offline && + !lvd->vdev_faulted && + !lvd->vdev_removed) + present++; + } + *req_writes += MAX(present, 1); } } - mmp_claim_uberblock_sync(zio, good_writes, req_writes, - ub, cvd, flags); + mmp_claim_uberblock_sync(zio, good_writes, issued_writes, + req_writes, ub, cvd, flags); } if (!vd->vdev_ops->vdev_op_leaf) @@ -596,6 +652,17 @@ mmp_claim_uberblock_sync(zio_t *zio, uint64_t *good_writes, if (vd->vdev_ops == &vdev_draid_spare_ops) return; + /* + * Count the writes actually issued alongside those required. A leaf + * the config expects present but which cannot be written is never + * issued one, so a shortfall here means a device is missing rather + * than failing. Gated exactly as good_writes is in + * mmp_claim_uberblock_sync_done() so that the two counts describe the + * same set of leaves and can be compared. + */ + if (vd->vdev_top->vdev_ms_array != 0) + (*issued_writes)++; + abd_t *ub_abd = abd_alloc_for_io(VDEV_UBERBLOCK_SIZE(vd), B_TRUE); abd_copy_from_buf(ub_abd, ub, sizeof (uberblock_t)); abd_zero_off(ub_abd, sizeof (uberblock_t), @@ -615,6 +682,7 @@ mmp_claim_uberblock(spa_t *spa, vdev_t *vd, uberblock_t *ub) { int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL; uint64_t good_writes = 0; + uint64_t issued_writes = 0; uint64_t req_writes = 0; zio_t *zio; @@ -625,7 +693,8 @@ mmp_claim_uberblock(spa_t *spa, vdev_t *vd, uberblock_t *ub) /* Sync the uberblock to all writeable leaves */ zio = zio_root(spa, NULL, NULL, flags); - mmp_claim_uberblock_sync(zio, &good_writes, &req_writes, ub, vd, flags); + mmp_claim_uberblock_sync(zio, &good_writes, &issued_writes, &req_writes, + ub, vd, flags); (void) zio_wait(zio); /* Flush the new uberblocks so they're immediately visible */ @@ -636,16 +705,31 @@ mmp_claim_uberblock(spa_t *spa, vdev_t *vd, uberblock_t *ub) spa_config_exit(spa, SCL_ALL, mmp_tag); zfs_dbgmsg("mmp: claiming uberblock, spa=%s txg=%llu seq=%llu " - "req_writes=%llu good_writes=%llu", spa_load_name(spa), + "req_writes=%llu issued_writes=%llu good_writes=%llu", + spa_load_name(spa), (u_longlong_t)ub->ub_txg, (u_longlong_t)MMP_SEQ(ub), - (u_longlong_t)req_writes, (u_longlong_t)good_writes); + (u_longlong_t)req_writes, (u_longlong_t)issued_writes, + (u_longlong_t)good_writes); /* * To guarantee visibility from a remote host we require a minimum * number of good writes. For raidz/draid vdevs parity+1 writes, for - * mirrors 2 writes, and for singletons 1 write. + * mirrors one write per leg the config expects present, and for + * singletons 1 write. + * + * Distinguish the two ways of falling short. Too few writes issued + * means a device the config expects present could not be written at + * all, which persists across retries and is what 'zhack mmp reclaim' + * recovers. Enough issued but too few good means the writes reached + * present devices and failed, which a retry may clear. */ - if (req_writes == 0 || good_writes < req_writes) + if (req_writes == 0) + return (SET_ERROR(EIO)); + + if (issued_writes < req_writes) + return (SET_ERROR(ENODEV)); + + if (good_writes < req_writes) return (SET_ERROR(EIO)); return (0); diff --git a/sys/contrib/openzfs/module/zfs/spa.c b/sys/contrib/openzfs/module/zfs/spa.c index 1ad94fd1bf7f..71a16330cecc 100644 --- a/sys/contrib/openzfs/module/zfs/spa.c +++ b/sys/contrib/openzfs/module/zfs/spa.c @@ -2525,7 +2525,7 @@ spa_load_spares(spa_t *spa) vd->vdev_top = vd; vd->vdev_aux = &spa->spa_spares; - if (vdev_open(vd) != 0) + if (vdev_open(vd, CRED()) != 0) continue; if (vdev_validate_aux(vd) == 0) @@ -2638,7 +2638,7 @@ spa_load_l2cache(spa_t *spa) spa_l2cache_activate(vd); - if (vdev_open(vd) != 0) + if (vdev_open(vd, CRED()) != 0) continue; (void) vdev_validate_aux(vd); @@ -3984,8 +3984,17 @@ spa_activity_check_duration(spa_t *spa, uberblock_t *ub) * - ENXIO - system hostid not set * - ESRCH - activity check skipped * - EREMOTEIO - activity check detected active pool + * - ENODEV - claim could not be written to a device the config expects + * - EIO - claim writes were issued to present devices and failed * - EINTR - activity check interrupted * - 0 - activity check detected no activity + * + * ENODEV and EIO are reported with ZPOOL_CONFIG_MMP_STATE set to + * MMP_STATE_ACTIVE even though no remote host was seen. Nothing is actually + * active in either case, but an older zpool(8) knows only the two existing + * states and reaches zfs_error_aux() with an uninitialized buffer for any + * other value, so the state is kept as one it understands and the real cause + * travels in the result. */ static void spa_activity_set_load_info(spa_t *spa, nvlist_t *label, mmp_state_t state, @@ -4056,6 +4065,20 @@ spa_ld_activity_result(spa_t *spa, int error, const char *state) /* Userspace expects EREMOTEIO for no system hostid */ error = EREMOTEIO; break; + case ENODEV: + cmn_err(CE_WARN, "pool '%s' could not claim every device the " + "config expects present, aborted import during %s; if a " + "device is permanently gone see 'zhack mmp reclaim'", + spa_load_name(spa), state); + /* Userspace expects EREMOTEIO for a failed claim */ + error = EREMOTEIO; + break; + case EIO: + cmn_err(CE_WARN, "pool '%s' had I/O errors writing the claim, " + "aborted import during %s", spa_load_name(spa), state); + /* Userspace expects EREMOTEIO for a failed claim */ + error = EREMOTEIO; + break; case EREMOTEIO: cmn_err(CE_WARN, "pool '%s' activity detected, aborted " "import during %s", spa_load_name(spa), state); @@ -4185,6 +4208,9 @@ spa_activity_check_tryimport(spa_t *spa, uberblock_t *spa_ub, * error results: * 0 - no activity detected * EREMOTEIO - remote activity detected + * ENODEV - the claim could not be written to a device the config + * expects to be present + * EIO - the claim writes were issued to present devices and failed * EINTR - user canceled the operation */ static int @@ -4266,7 +4292,13 @@ spa_activity_check_claim(spa_t *spa) if (error) { spa_load_failed(spa, "mmp: uberblock claim " "failed, error=%d", error); - error = SET_ERROR(EREMOTEIO); + /* + * ENODEV and EIO are both kept distinct from the + * EREMOTEIO returned when another host is seen below. + * Failing to write the claim is not evidence of a + * remote host, and only the ENODEV case has a + * recovery. + */ break; } @@ -4310,9 +4342,14 @@ out: spa->spa_mmp.mmp_claim_ns = gethrtime() - start_time; (void) spa_import_progress_set_mmp_check(spa_guid(spa), 0); - if (error == EREMOTEIO) { + /* + * A claim shortfall reaches userspace as EREMOTEIO exactly as remote + * activity does, so an older zpool(8) sees no change. The cause + * travels in the result for a zpool(8) which knows to read it. + */ + if (error == EREMOTEIO || error == ENODEV || error == EIO) { spa_activity_set_load_info(spa, mmp_label, - MMP_STATE_ACTIVE, 0, 0, EREMOTEIO); + MMP_STATE_ACTIVE, 0, 0, error); } else { spa_activity_set_load_info(spa, mmp_label, MMP_STATE_INACTIVE, spa_ub.ub_txg, MMP_SEQ(&spa_ub), 0); @@ -4637,7 +4674,7 @@ spa_ld_open_vdevs(spa_t *spa) MAX(zfs_max_missing_tvds, spa->spa_missing_tvds_allowed); spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); - error = vdev_open(spa->spa_root_vdev); + error = vdev_open(spa->spa_root_vdev, CRED()); spa_config_exit(spa, SCL_ALL, FTAG); if (spa->spa_missing_tvds != 0) { @@ -6889,7 +6926,7 @@ spa_validate_aux_devs(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode, vd->vdev_top = vd; - if ((error = vdev_open(vd)) == 0 && + if ((error = vdev_open(vd, CRED())) == 0 && (error = vdev_label_init(vd, crtxg, label)) == 0) { fnvlist_add_uint64(dev[i], ZPOOL_CONFIG_GUID, vd->vdev_guid); @@ -7682,10 +7719,7 @@ spa_export_common(const char *pool, int new_state, nvlist_t **oldconfig, spa_open_ref(spa, FTAG); spa_namespace_exit(FTAG); spa_async_suspend(spa); - if (spa->spa_zvol_taskq) { - zvol_remove_minors(spa, spa_name(spa), B_TRUE); - taskq_wait(spa->spa_zvol_taskq); - } + spa_namespace_enter(FTAG); spa->spa_export_thread = curthread; spa_close(spa, FTAG); @@ -7723,6 +7757,11 @@ spa_export_common(const char *pool, int new_state, nvlist_t **oldconfig, * that we are finshed. */ + if (spa->spa_zvol_taskq) { + zvol_remove_minors(spa, spa_name(spa), B_TRUE); + taskq_wait(spa->spa_zvol_taskq); + } + if (spa->spa_sync_on) { vdev_t *rvd = spa->spa_root_vdev; /* diff --git a/sys/contrib/openzfs/module/zfs/spa_config.c b/sys/contrib/openzfs/module/zfs/spa_config.c index 31216e9a7ccc..b7e1919c46cd 100644 --- a/sys/contrib/openzfs/module/zfs/spa_config.c +++ b/sys/contrib/openzfs/module/zfs/spa_config.c @@ -84,7 +84,7 @@ spa_config_remove(spa_config_dirent_t *dp) int flags = O_RDWR | O_TRUNC; zfs_file_t *fp; - error = zfs_file_open(dp->scd_path, flags, 0644, &fp); + error = zfs_file_open(dp->scd_path, flags, 0644, kcred, &fp); if (error == 0) { (void) zfs_file_fsync(fp, O_SYNC); (void) zfs_file_close(fp); @@ -127,7 +127,7 @@ spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl) * is instead truncated and overwritten in place. This way we always * have a consistent view of the data or a zero length file. */ - err = zfs_file_open(dp->scd_path, oflags, 0644, &fp); + err = zfs_file_open(dp->scd_path, oflags, 0644, kcred, &fp); if (err == 0) { err = zfs_file_write(fp, buf, buflen, NULL); if (err == 0) diff --git a/sys/contrib/openzfs/module/zfs/vdev.c b/sys/contrib/openzfs/module/zfs/vdev.c index 854b6da25f0a..eeeefb3d6c45 100644 --- a/sys/contrib/openzfs/module/zfs/vdev.c +++ b/sys/contrib/openzfs/module/zfs/vdev.c @@ -32,6 +32,7 @@ * Copyright (c) 2021, 2025, Klara, Inc. * Copyright (c) 2021, 2023 Hewlett Packard Enterprise Development LP. * Copyright (c) 2026, Seagate Technology, LLC. + * Copyright (c) 2026, TrueNAS. */ #include <sys/zfs_context.h> @@ -1715,11 +1716,12 @@ vdev_metaslab_init(vdev_t *vd, uint64_t txg) /* * The metaslab was marked as dirty at the end of * metaslab_init(). Remove it from the dirty list so that we - * can uninitialize and reinitialize it to the new class. + * can uninitialize and reinitialize it to the new class. It + * may be dirty in any txg slot, so clear them all. */ - if (txg != 0) { + for (int t = 0; t < TXG_SIZE; t++) { (void) txg_list_remove_this(&vd->vdev_ms_list, - slog_ms, txg); + slog_ms, t); } uint64_t sm_obj = space_map_object(slog_ms->ms_sm); metaslab_fini(slog_ms); @@ -1972,14 +1974,23 @@ vdev_load_child(void *arg) vd->vdev_load_error = vdev_load(vd); } +typedef struct { + vdev_t *voc_vdev; + cred_t *voc_cred; +} vdev_open_child_t; + static void vdev_open_child(void *arg) { - vdev_t *vd = arg; + vdev_open_child_t *voc = arg; + vdev_t *vd = voc->voc_vdev; vd->vdev_open_thread = curthread; - vd->vdev_open_error = vdev_open(vd); + vd->vdev_open_error = vdev_open(vd, voc->voc_cred); vd->vdev_open_thread = NULL; + + crfree(voc->voc_cred); + kmem_free(voc, sizeof (vdev_open_child_t)); } static boolean_t @@ -2013,7 +2024,8 @@ vdev_default_open_children_func(vdev_t *vd) * deadlock when the current thread is holding the spa_namespace_lock. */ static void -vdev_open_children_impl(vdev_t *vd, vdev_open_children_func_t *open_func) +vdev_open_children_impl(vdev_t *vd, cred_t *cred, + vdev_open_children_func_t *open_func) { int children = vd->vdev_children; @@ -2028,10 +2040,15 @@ vdev_open_children_impl(vdev_t *vd, vdev_open_children_func_t *open_func) continue; if (tq == NULL || vdev_uses_zvols(vd)) { - cvd->vdev_open_error = vdev_open(cvd); + cvd->vdev_open_error = vdev_open(cvd, cred); } else { + vdev_open_child_t *voc = + kmem_alloc(sizeof (vdev_open_child_t), KM_SLEEP); + voc->voc_vdev = cvd; + voc->voc_cred = cred; + crhold(cred); VERIFY(taskq_dispatch(tq, vdev_open_child, - cvd, TQ_SLEEP) != TASKQID_INVALID); + voc, TQ_SLEEP) != TASKQID_INVALID); } } @@ -2054,18 +2071,19 @@ vdev_open_children_impl(vdev_t *vd, vdev_open_children_func_t *open_func) * Open all child vdevs. */ void -vdev_open_children(vdev_t *vd) +vdev_open_children(vdev_t *vd, cred_t *cred) { - vdev_open_children_impl(vd, vdev_default_open_children_func); + vdev_open_children_impl(vd, cred, vdev_default_open_children_func); } /* * Conditionally open a subset of child vdevs. */ void -vdev_open_children_subset(vdev_t *vd, vdev_open_children_func_t *open_func) +vdev_open_children_subset(vdev_t *vd, cred_t *cred, + vdev_open_children_func_t *open_func) { - vdev_open_children_impl(vd, open_func); + vdev_open_children_impl(vd, cred, open_func); } /* @@ -2141,7 +2159,7 @@ vdev_ashift_optimize(vdev_t *vd) * Prepare a virtual device for access. */ int -vdev_open(vdev_t *vd) +vdev_open(vdev_t *vd, cred_t *cred) { spa_t *spa = vd->vdev_spa; int error; @@ -2182,7 +2200,7 @@ vdev_open(vdev_t *vd) } error = vd->vdev_ops->vdev_op_open(vd, &osize, &max_osize, - &logical_ashift, &physical_ashift); + &logical_ashift, &physical_ashift, cred); /* Keep the device in removed state if unplugged */ if (error == ENOENT && vd->vdev_removed) { @@ -2874,7 +2892,7 @@ vdev_reopen(vdev_t *vd) /* set the reopening flag unless we're taking the vdev offline */ vd->vdev_reopening = !vd->vdev_offline; vdev_close(vd); - (void) vdev_open(vd); + (void) vdev_open(vd, CRED()); /* * Call vdev_validate() here to make sure we have the same device. @@ -2929,7 +2947,7 @@ vdev_create(vdev_t *vd, uint64_t txg, boolean_t isreplacing) * For a create, however, we want to fail the request if * there are any components we can't open. */ - error = vdev_open(vd); + error = vdev_open(vd, CRED()); if (error || vd->vdev_state != VDEV_STATE_HEALTHY) { vdev_close(vd); diff --git a/sys/contrib/openzfs/module/zfs/vdev_draid.c b/sys/contrib/openzfs/module/zfs/vdev_draid.c index 576d88c3c7b4..ee17283025bb 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_draid.c +++ b/sys/contrib/openzfs/module/zfs/vdev_draid.c @@ -1523,7 +1523,7 @@ vdev_draid_open_children(vdev_t *vd) */ static int vdev_draid_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { vdev_draid_config_t *vdc = vd->vdev_tsd; uint64_t nparity = vdc->vdc_nparity; @@ -1540,8 +1540,8 @@ vdev_draid_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, * ordering is important to ensure the distributed spares calculate * the correct psize in the event that the dRAID vdevs were expanded. */ - vdev_open_children_subset(vd, vdev_draid_open_children); - vdev_open_children_subset(vd, vdev_draid_open_spares); + vdev_open_children_subset(vd, cr, vdev_draid_open_children); + vdev_open_children_subset(vd, cr, vdev_draid_open_spares); /* Verify enough of the children are available to continue. */ for (int c = 0; c < vd->vdev_children; c++) { @@ -2466,8 +2466,9 @@ vdev_draid_spare_close(vdev_t *vd) */ static int vdev_draid_spare_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { + (void) cr; vdev_draid_spare_t *vds = vd->vdev_tsd; vdev_t *rvd = vd->vdev_spa->spa_root_vdev; uint64_t asize, max_asize; diff --git a/sys/contrib/openzfs/module/zfs/vdev_file.c b/sys/contrib/openzfs/module/zfs/vdev_file.c index 20b4db65ec06..485117f1729d 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_file.c +++ b/sys/contrib/openzfs/module/zfs/vdev_file.c @@ -23,6 +23,7 @@ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright (c) 2025, Klara, Inc. + * Copyright (c) 2026, TrueNAS. */ #include <sys/zfs_context.h> @@ -97,7 +98,7 @@ vdev_file_open_mode(spa_mode_t spa_mode) static int vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { vdev_file_t *vf; zfs_file_t *fp; @@ -152,7 +153,8 @@ vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, ASSERT3S(vd->vdev_path[0], ==, '/'); error = zfs_file_open(vd->vdev_path, - vdev_file_open_mode(spa_mode(vd->vdev_spa)), 0, &fp); + vdev_file_open_mode(spa_mode(vd->vdev_spa)), 0, cr, &fp); + if (error) { vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; return (error); diff --git a/sys/contrib/openzfs/module/zfs/vdev_indirect.c b/sys/contrib/openzfs/module/zfs/vdev_indirect.c index 7538f471e63c..b7f8c35c09a8 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_indirect.c +++ b/sys/contrib/openzfs/module/zfs/vdev_indirect.c @@ -949,8 +949,9 @@ vdev_indirect_close(vdev_t *vd) static int vdev_indirect_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { + (void) cr; *psize = *max_psize = vd->vdev_asize + VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE; *logical_ashift = vd->vdev_ashift; diff --git a/sys/contrib/openzfs/module/zfs/vdev_mirror.c b/sys/contrib/openzfs/module/zfs/vdev_mirror.c index 35a4a5bebecf..ea31866ff13c 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_mirror.c +++ b/sys/contrib/openzfs/module/zfs/vdev_mirror.c @@ -386,7 +386,7 @@ vdev_mirror_map_init(zio_t *zio) static int vdev_mirror_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { int numerrors = 0; int lasterror = 0; @@ -396,7 +396,7 @@ vdev_mirror_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, return (SET_ERROR(EINVAL)); } - vdev_open_children(vd); + vdev_open_children(vd, cr); for (int c = 0; c < vd->vdev_children; c++) { vdev_t *cvd = vd->vdev_child[c]; diff --git a/sys/contrib/openzfs/module/zfs/vdev_missing.c b/sys/contrib/openzfs/module/zfs/vdev_missing.c index c62faef2d05c..fad77f8bcb1b 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_missing.c +++ b/sys/contrib/openzfs/module/zfs/vdev_missing.c @@ -45,7 +45,7 @@ static int vdev_missing_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, - uint64_t *ashift, uint64_t *pshift) + uint64_t *ashift, uint64_t *pshift, cred_t *cr) { /* * Really this should just fail. But then the root vdev will be in the @@ -53,7 +53,7 @@ vdev_missing_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, * VDEV_AUX_BAD_GUID_SUM. So we pretend to succeed, knowing that we * will fail the GUID sum check before ever trying to open the pool. */ - (void) vd; + (void) vd, (void) cr; *psize = 0; *max_psize = 0; *ashift = 0; diff --git a/sys/contrib/openzfs/module/zfs/vdev_raidz.c b/sys/contrib/openzfs/module/zfs/vdev_raidz.c index 6eb8d44cb286..f7bfb79fef6c 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_raidz.c +++ b/sys/contrib/openzfs/module/zfs/vdev_raidz.c @@ -2165,7 +2165,7 @@ vdev_raidz_reconstruct_row(raidz_map_t *rm, raidz_row_t *rr, static int vdev_raidz_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, - uint64_t *logical_ashift, uint64_t *physical_ashift) + uint64_t *logical_ashift, uint64_t *physical_ashift, cred_t *cr) { vdev_raidz_t *vdrz = vd->vdev_tsd; uint64_t nparity = vdrz->vd_nparity; @@ -2181,7 +2181,7 @@ vdev_raidz_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, return (SET_ERROR(EINVAL)); } - vdev_open_children(vd); + vdev_open_children(vd, cr); for (c = 0; c < vd->vdev_children; c++) { vdev_t *cvd = vd->vdev_child[c]; diff --git a/sys/contrib/openzfs/module/zfs/vdev_removal.c b/sys/contrib/openzfs/module/zfs/vdev_removal.c index e222f3f2dce0..326a8fe0d92d 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_removal.c +++ b/sys/contrib/openzfs/module/zfs/vdev_removal.c @@ -1432,14 +1432,17 @@ vdev_remove_complete(spa_t *spa) vdev_remove_replace_with_indirect(vd, txg); /* - * We now release the locks, allowing spa_sync to run and finish the + * Release the config lock and allow spa_sync to run and finish the * removal via vdev_remove_complete_sync in syncing context. * + * Retain the namespace lock to prevent the pool from being exported + * or destroyed before completion of the device removal. + * * Note that we hold on to the vdev_t that has been replaced. Since * it isn't part of the vdev tree any longer, it can't be concurrently * manipulated, even while we don't have the config lock. */ - (void) spa_vdev_exit(spa, NULL, txg, 0); + (void) spa_vdev_config_exit(spa, NULL, txg, 0, FTAG); /* * Top ZAP should have been transferred to the indirect vdev in @@ -1452,7 +1455,8 @@ vdev_remove_complete(spa_t *spa) */ ASSERT0(vd->vdev_leaf_zap); - txg = spa_vdev_enter(spa); + /* Update the vdev labels and dirty the config. */ + txg = spa_vdev_config_enter(spa); (void) vdev_label_init(vd, 0, VDEV_LABEL_REMOVE); /* * Request to update the config and the config cachefile. diff --git a/sys/contrib/openzfs/module/zfs/vdev_root.c b/sys/contrib/openzfs/module/zfs/vdev_root.c index 21a81d6d25b9..cb8116e2bb3e 100644 --- a/sys/contrib/openzfs/module/zfs/vdev_root.c +++ b/sys/contrib/openzfs/module/zfs/vdev_root.c @@ -83,7 +83,7 @@ too_many_errors(vdev_t *vd, uint64_t numerrors) static int vdev_root_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, - uint64_t *ashift, uint64_t *pshift) + uint64_t *ashift, uint64_t *pshift, cred_t *cr) { spa_t *spa = vd->vdev_spa; int lasterror = 0; @@ -94,7 +94,7 @@ vdev_root_open(vdev_t *vd, uint64_t *asize, uint64_t *max_asize, return (SET_ERROR(EINVAL)); } - vdev_open_children(vd); + vdev_open_children(vd, cr); for (int c = 0; c < vd->vdev_children; c++) { vdev_t *cvd = vd->vdev_child[c]; diff --git a/sys/contrib/openzfs/module/zfs/zfs_crrd.c b/sys/contrib/openzfs/module/zfs/zfs_crrd.c index 30d4c7c36897..8aee0727161f 100644 --- a/sys/contrib/openzfs/module/zfs/zfs_crrd.c +++ b/sys/contrib/openzfs/module/zfs/zfs_crrd.c @@ -99,14 +99,14 @@ rrd_tail(rrd_t *rrd) * rrd_get works from 0..rrd_len()-1. */ size_t -rrd_len(rrd_t *rrd) +rrd_len(const rrd_t *rrd) { return (rrd->rrd_length); } const rrd_data_t * -rrd_entry(rrd_t *rrd, size_t i) +rrd_entry(const rrd_t *rrd, size_t i) { size_t n; @@ -119,7 +119,7 @@ rrd_entry(rrd_t *rrd, size_t i) } uint64_t -rrd_get(rrd_t *rrd, size_t i) +rrd_get(const rrd_t *rrd, size_t i) { const rrd_data_t *data = rrd_entry(rrd, i); @@ -226,3 +226,19 @@ dbrrd_query(dbrrd_t *r, hrtime_t tv, dbrrd_rounding_t rounding) return (data == NULL ? 0 : data->rrdd_txg); } + +hrtime_t +dbrrd_latest_time(dbrrd_t *r) +{ + const rrd_data_t *head; + const rrd_t *curdb; + size_t dblen; + + curdb = &r->dbr_minutes; + dblen = rrd_len(curdb); + if (dblen == 0) + return (0); + + head = rrd_entry(curdb, dblen - 1); + return (head->rrdd_time); +} diff --git a/sys/contrib/openzfs/module/zfs/zfs_ioctl.c b/sys/contrib/openzfs/module/zfs/zfs_ioctl.c index 88ae044573b7..6922631381b7 100644 --- a/sys/contrib/openzfs/module/zfs/zfs_ioctl.c +++ b/sys/contrib/openzfs/module/zfs/zfs_ioctl.c @@ -678,6 +678,47 @@ zfs_secpolicy_rollback(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) } static int +zfs_secpolicy_send_impl(const char *name, dsl_dataset_t *ds, cred_t *cr, + boolean_t rawok) +{ + /* Can't send from within a zone that can't see the dataset */ + int err = zfs_dozonecheck_ds(name, ds, cr); + if (err != 0) + return (err); + + /* ZFS global admin (root) can do anything. */ + err = secpolicy_zfs(cr); + if (err == 0) + return (0); + + /* 'send' permission on this dataset is allowed to send. */ + err = dsl_deleg_access_impl(ds, ZFS_DELEG_PERM_SEND, cr); + if (err == 0) + return (0); + + /* Raw sends have extra perms that might work. */ + if (rawok) { + /* 'send:raw' permission on this dataset can do raw sends. */ + err = dsl_deleg_access_impl(ds, ZFS_DELEG_PERM_SEND_RAW, cr); + if (err == 0) + return (0); + + if (ds->ds_dir->dd_crypto_obj != 0) { + /* + * Dataset is encrypted; 'send:encrypted' permission + * will allow a raw send. + */ + err = dsl_deleg_access_impl(ds, + ZFS_DELEG_PERM_SEND_ENCRYPTED, cr); + if (err == 0) + return (0); + } + } + + return (err); +} + +static int zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) { (void) innvl; @@ -706,12 +747,8 @@ zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) dsl_dataset_name(ds, zc->zc_name); - error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, - ZFS_DELEG_PERM_SEND, cr); - if (error != 0 && rawok) { - error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, - ZFS_DELEG_PERM_SEND_RAW, cr); - } + error = zfs_secpolicy_send_impl(zc->zc_name, ds, cr, rawok); + dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); @@ -721,16 +758,29 @@ zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) static int zfs_secpolicy_send_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) { - boolean_t rawok = nvlist_exists(innvl, "rawok"); + dsl_pool_t *dp; + dsl_dataset_t *ds; int error; + boolean_t rawok = nvlist_exists(innvl, "rawok"); - (void) innvl; - error = zfs_secpolicy_write_perms(zc->zc_name, - ZFS_DELEG_PERM_SEND, cr); - if (error != 0 && rawok) { - error = zfs_secpolicy_write_perms(zc->zc_name, - ZFS_DELEG_PERM_SEND_RAW, cr); + if (INGLOBALZONE(curproc) && secpolicy_zfs(cr) == 0) + return (0); + + error = dsl_pool_hold(zc->zc_name, FTAG, &dp); + if (error != 0) + return (error); + + error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds); + if (error != 0) { + dsl_pool_rele(dp, FTAG); + return (error); } + + error = zfs_secpolicy_send_impl(zc->zc_name, ds, cr, rawok); + + dsl_dataset_rele(ds, FTAG); + dsl_pool_rele(dp, FTAG); + return (error); } @@ -1443,8 +1493,17 @@ zfsvfs_hold(const char *name, const void *tag, zfsvfs_t **zfvp, int error = 0; if (getzfsvfs(name, zfvp) != 0) - error = zfsvfs_create(name, B_FALSE, zfvp); + error = zfsvfs_create_hold(name, zfvp); if (error == 0) { + /* + * dmu_objset_hold() keeps the pool config read lock held. + * Drop it before acquiring the teardown lock to avoid ABBA + * deadlock with zfs_resume_fs(), which holds teardown write + * then acquires the config lock. + */ + if ((*zfvp)->z_use_hold) + dsl_pool_config_exit( + dmu_objset_pool((*zfvp)->z_os), *zfvp); if (writer) ZFS_TEARDOWN_ENTER_WRITE(*zfvp, tag); else @@ -1471,7 +1530,18 @@ zfsvfs_rele(zfsvfs_t *zfsvfs, const void *tag) if (zfs_vfs_held(zfsvfs)) { zfs_vfs_rele(zfsvfs); } else { - dmu_objset_disown(zfsvfs->z_os, B_TRUE, zfsvfs); + objset_t *os = zfsvfs->z_os; + if (zfsvfs->z_use_hold) { + /* + * Opened via dmu_objset_hold(): re-acquire the pool + * config lock (released in zfsvfs_hold() before the + * teardown lock) so that dmu_objset_rele() can exit it. + */ + dsl_pool_config_enter(dmu_objset_pool(os), zfsvfs); + dmu_objset_rele(os, zfsvfs); + } else { + dmu_objset_disown(os, B_TRUE, zfsvfs); + } zfsvfs_free(zfsvfs); } } diff --git a/sys/contrib/openzfs/module/zfs/zfs_vnops.c b/sys/contrib/openzfs/module/zfs/zfs_vnops.c index 7bb9ba57c69e..e773ea11bd11 100644 --- a/sys/contrib/openzfs/module/zfs/zfs_vnops.c +++ b/sys/contrib/openzfs/module/zfs/zfs_vnops.c @@ -919,7 +919,15 @@ zfs_write(znode_t *zp, zfs_uio_t *uio, int ioflag, cred_t *cr) * error, and we may break the loop early. */ n -= tx_bytes - zfs_uio_resid(uio); - pfbytes -= tx_bytes - zfs_uio_resid(uio); + /* + * The prefaulted pages still faulted, so force + * a re-prefault on the next pass. If they can + * no longer be faulted in (e.g. the calling + * process is exiting), zfs_uio_prefaultpages() + * fails and the loop breaks with EFAULT instead + * of spinning on them forever. + */ + pfbytes = 0; continue; } #endif @@ -1879,7 +1887,7 @@ zfs_clone_range(znode_t *inzp, uint64_t *inoffp, znode_t *outzp, /* * Block growth may fail for many reasons we can not - * predict here. If it happen the cloning is doomed. + * predict here. If it happens, the cloning is doomed. */ if (inblksz != outzp->z_blksz) { error = SET_ERROR(EINVAL); @@ -2039,7 +2047,19 @@ zfs_clone_range_replay(znode_t *zp, uint64_t off, uint64_t len, uint64_t blksz, if (zp->z_blksz < blksz) zfs_grow_blocksize(zp, blksz, tx); - dmu_brt_clone(zfsvfs->z_os, zp->z_id, off, len, tx, bps, nbps); + if (blksz != zp->z_blksz) { + error = SET_ERROR(EINVAL); + dmu_tx_commit(tx); + zfs_exit(zfsvfs, FTAG); + return (error); + } + + error = dmu_brt_clone(zfsvfs->z_os, zp->z_id, off, len, tx, bps, nbps); + if (error != 0) { + dmu_tx_commit(tx); + zfs_exit(zfsvfs, FTAG); + return (error); + } zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime); diff --git a/sys/contrib/openzfs/module/zfs/zio.c b/sys/contrib/openzfs/module/zfs/zio.c index cef79e6490ee..8cabb3c9bea9 100644 --- a/sys/contrib/openzfs/module/zfs/zio.c +++ b/sys/contrib/openzfs/module/zfs/zio.c @@ -1737,6 +1737,15 @@ zio_flush(zio_t *pio, vdev_t *vd) return; if (vd->vdev_children == 0) { + /* + * A non-concrete vdev (a hole or indirect vdev left behind + * by removing a log or data device) has no leaf device to + * flush. Skip it; issuing a flush to an indirect vdev would + * trip the ZIO_TYPE_WRITE assertion in + * vdev_indirect_io_start(). + */ + if (!vdev_is_concrete(vd)) + return; zio_nowait(zio_create(pio, vd->vdev_spa, 0, NULL, NULL, 0, 0, NULL, NULL, ZIO_TYPE_FLUSH, ZIO_PRIORITY_NOW, flags, vd, 0, NULL, ZIO_STAGE_OPEN, ZIO_FLUSH_PIPELINE)); @@ -3594,16 +3603,26 @@ zio_ddt_collision(zio_t *zio, ddt_t *ddt, ddt_entry_t *dde) if (dde->dde_io == NULL) continue; + /* + * Lock dde_io to prevent the lead zio from completing + * and freeing its ABD while we compare against it. + */ + mutex_enter(&dde->dde_io->dde_io_lock); zio_t *lio = dde->dde_io->dde_lead_zio[p]; - if (lio == NULL) + if (lio == NULL) { + mutex_exit(&dde->dde_io->dde_io_lock); continue; - - if (do_raw) - return (lio->io_size != zio->io_size || - abd_cmp(zio->io_abd, lio->io_abd) != 0); - - return (lio->io_orig_size != zio->io_orig_size || - abd_cmp(zio->io_orig_abd, lio->io_orig_abd) != 0); + } + boolean_t collision; + if (do_raw) { + collision = lio->io_size != zio->io_size || + abd_cmp(zio->io_abd, lio->io_abd) != 0; + } else { + collision = lio->io_orig_size != zio->io_orig_size || + abd_cmp(zio->io_orig_abd, lio->io_orig_abd) != 0; + } + mutex_exit(&dde->dde_io->dde_io_lock); + return (collision); } for (int p = 0; p < DDT_NPHYS(ddt); p++) { @@ -3837,7 +3856,6 @@ zio_ddt_write(zio_t *zio) int p = DDT_PHYS_FOR_COPIES(ddt, zp->zp_copies); ddt_phys_variant_t v = DDT_PHYS_VARIANT(ddt, p); - ddt_univ_phys_t *ddp = dde->dde_phys; /* * In the common cases, at this point we have a regular BP with no @@ -3868,14 +3886,6 @@ zio_ddt_write(zio_t *zio) * end of the chain and letting the sequence play out. */ - /* - * Number of DVAs in the DDT entry. If the BP is encrypted we ignore - * the third one as normal. - */ - int have_dvas = ddt_phys_dva_count(ddp, v, BP_IS_ENCRYPTED(bp)); - IMPLY(have_dvas == 0, ddt_phys_birth(ddp, v) == 0); - boolean_t is_ganged = ddt_phys_is_gang(ddp, v); - /* Number of DVAs requested by the IO. */ uint8_t need_dvas = zp->zp_copies; /* Number of DVAs in outstanding writes for this dde. */ @@ -3890,6 +3900,21 @@ zio_ddt_write(zio_t *zio) if (dde_io != NULL) mutex_enter(&dde_io->dde_io_lock); + /* + * Number of DVAs in the DDT entry. If the BP is encrypted we ignore + * the third one as normal. + * + * Must be computed after taking dde_io_lock (if held) to avoid + * racing with ddt_phys_unextend() in zio_ddt_child_write_done() + * error path, which can zero DVAs under dde_io_lock. Without the + * lock, a stale have_dvas causes ddt_bp_fill() to copy a zeroed + * DVA into the BP, producing a hole that reads back as zeros. + */ + ddt_univ_phys_t *ddp = dde->dde_phys; + int have_dvas = ddt_phys_dva_count(ddp, v, BP_IS_ENCRYPTED(bp)); + IMPLY(have_dvas == 0, ddt_phys_birth(ddp, v) == 0); + boolean_t is_ganged = ddt_phys_is_gang(ddp, v); + if (dde_io == NULL || dde_io->dde_lead_zio[p] == NULL) { /* * No IO outstanding, so we only need to worry about ourselves. @@ -5338,16 +5363,18 @@ zio_dio_chksum_verify_error_report(zio_t *zio) */ zio->io_error = SET_ERROR(EIO); /* - * Report dio_verify_wr ZED event. + * Report dio_verify_wr ZED event, rate limited. */ - (void) zfs_ereport_post(FM_EREPORT_ZFS_DIO_VERIFY_WR, - zio->io_spa, zio->io_vd, &zio->io_bookmark, zio, 0); + if (zfs_ratelimit(&zio->io_vd->vdev_dio_verify_rl)) + (void) zfs_ereport_post(FM_EREPORT_ZFS_DIO_VERIFY_WR, + zio->io_spa, zio->io_vd, &zio->io_bookmark, zio, 0); } else { /* - * Report dio_verify_rd ZED event. + * Report dio_verify_rd ZED event, rate limited. */ - (void) zfs_ereport_post(FM_EREPORT_ZFS_DIO_VERIFY_RD, - zio->io_spa, zio->io_vd, &zio->io_bookmark, zio, 0); + if (zfs_ratelimit(&zio->io_vd->vdev_dio_verify_rl)) + (void) zfs_ereport_post(FM_EREPORT_ZFS_DIO_VERIFY_RD, + zio->io_spa, zio->io_vd, &zio->io_bookmark, zio, 0); } } @@ -5973,6 +6000,67 @@ zbookmark_compare(uint16_t dbss1, uint8_t ibs1, uint16_t dbss2, uint8_t ibs2, zb1->zb_blkid == zb2->zb_blkid) return (0); + if (zb1->zb_level < 0 || zb2->zb_level < 0) { + /* + * "Negative" levels are ZB_ROOT_LEVEL, ZB_ZIL_LEVEL or + * ZB_DNODE_LEVEL, and represent some sort of auxiliary dataset + * block or object. In this case, we're usually being called + * from dsl_scan or dmu_traverse. + * + * These "levels" are more like a "type" signal, not directly + * comparable, but we have to do something. So we order them in + * the order we would see them during a typical scan or + * traverse: + * + * - ZB_ROOT_LEVEL: the "top" block carrying the dataset head + * - ZB_ZIL_LEVEL: the head ZIL block attached to the dataset + * - ZB_DNODE_LEVEL: "virtual" position representing an + * entire object. Sorts ahead of the true + * data blocks for the object. + * - level >= 0: data blocks + * + * We work through these cases from top to bottom, with + * appropriate tiebreaks for each kind. + */ + + /* + * Root level wins. It shouldn't be possible for both to be the + * root level in this per-dataset tree, and there's no obvious + * tiebreaker, but we handle it as a defensive measure. + */ + if (zb1->zb_level == ZB_ROOT_LEVEL && + zb2->zb_level == ZB_ROOT_LEVEL) + return (TREE_PCMP(zb1, zb2)); + if (zb1->zb_level == ZB_ROOT_LEVEL) + return (-1); + if (zb2->zb_level == ZB_ROOT_LEVEL) + return (1); + + /* ZIL bookmarks have valid blkid, so the earlier one wins. */ + if (zb1->zb_level == ZB_ZIL_LEVEL && + zb2->zb_level == ZB_ZIL_LEVEL) + return (TREE_CMP(zb1->zb_blkid, zb2->zb_blkid)); + if (zb1->zb_level == ZB_ZIL_LEVEL) + return (-1); + if (zb2->zb_level == ZB_ZIL_LEVEL) + return (1); + + /* + * If we get this far, then at least one is ZB_DNODE_LEVEL, and + * the other is either ZB_DNODE_LEVEL or a data block. + * Regardless, the one with the lower-numbered object wins - + * earler ZB_DNODE_LEVEL beats later, but data block on earlier + * objects beats the virtual marker on later objects. + */ + int cmp = TREE_CMP(zb1->zb_object, zb2->zb_object); + if (cmp != 0) + return (cmp); + + if (zb1->zb_level == ZB_DNODE_LEVEL) + return (-1); + return (1); + } + IMPLY(zb1->zb_level > 0, ibs1 >= SPA_MINBLOCKSHIFT); IMPLY(zb2->zb_level > 0, ibs2 >= SPA_MINBLOCKSHIFT); diff --git a/sys/contrib/openzfs/module/zfs/zvol.c b/sys/contrib/openzfs/module/zfs/zvol.c index e1fe3eae2f27..8af7559ecb66 100644 --- a/sys/contrib/openzfs/module/zfs/zvol.c +++ b/sys/contrib/openzfs/module/zfs/zvol.c @@ -1262,6 +1262,13 @@ zvol_first_open(zvol_state_t *zv, boolean_t readonly) if (error) { dmu_objset_disown(os, 1, zv); zv->zv_objset = NULL; + } else { + /* + * Take a hold on the spa so that spa_export_common() will + * return EBUSY while the zvol block device is open, just + * as it does for mounted datasets. + */ + spa_open_ref(dmu_objset_spa(os), zv); } return (error); @@ -1276,10 +1283,14 @@ zvol_last_close(zvol_state_t *zv) if (zv->zv_flags & ZVOL_REMOVING) cv_broadcast(&zv->zv_removing_cv); + spa_t *spa = dmu_objset_spa(zv->zv_objset); + zvol_shutdown_zv(zv); dmu_objset_disown(zv->zv_objset, 1, zv); zv->zv_objset = NULL; + + spa_close(spa, zv); } typedef struct minors_job { diff --git a/sys/contrib/openzfs/scripts/cstyle.pl b/sys/contrib/openzfs/scripts/cstyle.pl index 5a32ccc4e988..de29edf30b83 100755 --- a/sys/contrib/openzfs/scripts/cstyle.pl +++ b/sys/contrib/openzfs/scripts/cstyle.pl @@ -97,6 +97,9 @@ $hdr_comment_start = qr/^\s*\/\*$/; my $typename = '(int|char|short|long|unsigned|float|double' . '|\w+_t|struct\s+\w+|union\s+\w+|FILE)'; +# cast or compound-literal type, e.g. "(foo_t *)" +my $cast = qr/\($typename(?: \*+)?\)/; + # mapping of old types to POSIX compatible types my %old2posix = ( 'unchar' => 'uchar_t', @@ -683,12 +686,14 @@ line: while (<$filehandle>) { } if ($picky) { # try to detect spaces after casts, but allow (e.g.) - # "sizeof (int) + 1", "void (*funcptr)(int) = foo;", and - # "int foo(int) __NORETURN;" - if ((/^\($typename( \*+)?\)\s/o || - /\W\($typename( \*+)?\)\s/o) && - !/sizeof\s*\($typename( \*)?\)\s/o && - !/\($typename( \*+)?\)\s+=[^=]/o) { + # "sizeof (int) + 1", "void (*funcptr)(int) = foo;", + # "int foo(int) __NORETURN;", and "(foo_t) { ... }" + # + # sizeof (type) is not a cast; remove it so it can't mask + # a real cast elsewhere on the line. + my $tmp = $_; + $tmp =~ s/\bsizeof\s*$cast/sizeof/g; + if ($tmp =~ /(?:^|\W)$cast(?>\s+)(?!\{|=[^=])/) { err("space after cast"); } if (/\b$typename\s*\*\s/o && @@ -801,12 +806,18 @@ process_indent($) my $case = 'case\b[^:]*$'; # skip over enumerations, array definitions, initializers, etc. - if ($cont_off <= 0 && !/^\s*$special/ && - (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*))\{/ || - (/^\s*\{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) { - $cont_in = 0; - $cont_off = tr/{/{/ - tr/}/}/; - return; + if ($cont_off <= 0 && !/^\s*$special/) { + my $aggregate = /\b(?:enum|struct|union)\s*[^{]*\{/; + my $initializer = /\s+=\s*(?:$cast\s*)?\{/; + # "= {" split across lines, "=" possibly trailed by comments + my $split_init = /^\s*\{/ && + $prev =~ /=\s*(?:$cast\s*)?(?:\/\*.*\*\/\s*)?$/; + + if ($aggregate || $initializer || $split_init) { + $cont_in = 0; + $cont_off = tr/{/{/ - tr/}/}/; + return; + } } if ($cont_off) { $cont_off += tr/{/{/ - tr/}/}/; diff --git a/sys/contrib/openzfs/tests/runfiles/common.run b/sys/contrib/openzfs/tests/runfiles/common.run index 1903fb606318..663420e57775 100644 --- a/sys/contrib/openzfs/tests/runfiles/common.run +++ b/sys/contrib/openzfs/tests/runfiles/common.run @@ -296,6 +296,7 @@ tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos', 'zfs_receive_from_encrypted', 'zfs_receive_to_encrypted', 'zfs_receive_raw', 'zfs_receive_raw_incremental', 'zfs_receive_-e', 'zfs_receive_raw_-d', 'zfs_receive_from_zstd', 'zfs_receive_new_props', + 'zfs_receive-x_props_alternation', 'zfs_receive_-wR-encrypted-mix', 'zfs_receive_corrective', 'zfs_receive_compressed_corrective', 'zfs_receive_large_block_corrective'] tags = ['functional', 'cli_root', 'zfs_receive'] @@ -329,10 +330,6 @@ tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos', 'zfs_send_raw', 'zfs_send_sparse', 'zfs_send-b', 'zfs_send_skip_missing'] tags = ['functional', 'cli_root', 'zfs_send'] -[tests/functional/cli_root/zfs_send_delegation] -tests = ['zfs_send_test'] -tags = ['functional', 'cli_root', 'zfs_send_delegation'] - [tests/functional/cli_root/zfs_set] tests = ['cache_001_pos', 'cache_002_neg', 'canmount_001_pos', 'canmount_002_pos', 'canmount_003_pos', 'canmount_004_pos', @@ -455,7 +452,7 @@ tags = ['functional', 'cli_root', 'zpool_events'] [tests/functional/cli_root/zpool_export] tests = ['zpool_export_001_pos', 'zpool_export_002_pos', 'zpool_export_003_neg', 'zpool_export_004_pos', - 'zpool_export_parallel_pos', 'zpool_export_parallel_admin'] + 'zpool_export_005_neg', 'zpool_export_parallel_pos', 'zpool_export_parallel_admin'] tags = ['functional', 'cli_root', 'zpool_export'] [tests/functional/cli_root/zpool_get] @@ -583,6 +580,7 @@ tests = ['zpool_status_001_pos', 'zpool_status_002_pos', 'zpool_status_003_pos', 'zpool_status_004_pos', 'zpool_status_005_pos', 'zpool_status_006_pos', 'zpool_status_007_pos', 'zpool_status_008_pos', + 'zpool_status_009_pos', 'zpool_status_features_001_pos'] tags = ['functional', 'cli_root', 'zpool_status'] @@ -652,10 +650,6 @@ tests = ['zfs_list_001_pos', 'zfs_list_002_pos', 'zfs_list_003_pos', user = tags = ['functional', 'cli_user', 'zfs_list'] -[tests/functional/cli_user/zfs_send_delegation_user] -tests = ['zfs_send_usertest'] -tags = ['functional', 'cli_user', 'zfs_send_delegation_user'] - [tests/functional/cli_user/zpool_iostat] tests = ['zpool_iostat_001_neg', 'zpool_iostat_002_pos', 'zpool_iostat_003_neg', 'zpool_iostat_004_pos', @@ -723,9 +717,9 @@ tests = ['zfs_allow_001_pos', 'zfs_allow_002_pos', 'zfs_allow_003_pos', 'zfs_allow_004_pos', 'zfs_allow_005_pos', 'zfs_allow_006_pos', 'zfs_allow_007_pos', 'zfs_allow_008_pos', 'zfs_allow_009_neg', 'zfs_allow_010_pos', 'zfs_allow_011_neg', 'zfs_allow_012_neg', - 'zfs_unallow_001_pos', 'zfs_unallow_002_pos', 'zfs_unallow_003_pos', - 'zfs_unallow_004_pos', 'zfs_unallow_005_pos', 'zfs_unallow_006_pos', - 'zfs_unallow_007_neg', 'zfs_unallow_008_neg'] + 'zfs_allow_send', 'zfs_unallow_001_pos', 'zfs_unallow_002_pos', + 'zfs_unallow_003_pos', 'zfs_unallow_004_pos', 'zfs_unallow_005_pos', + 'zfs_unallow_006_pos', 'zfs_unallow_007_neg', 'zfs_unallow_008_neg'] tags = ['functional', 'delegate'] [tests/functional/direct] @@ -880,7 +874,7 @@ tests = ['defaultprojectquota_002_pos', 'defaultprojectquota_003_neg', 'defaultprojectquota_007_pos', 'projectquota_002_pos', 'projectquota_004_neg', 'projectquota_005_pos', 'projectquota_007_pos', 'projectquota_008_pos', 'projectquota_009_pos', 'projecttree_002_pos', - 'projecttree_003_neg'] + 'projecttree_003_neg', 'projectspace_006_pos'] tags = ['functional', 'projectquota'] [tests/functional/poolversion] @@ -1003,8 +997,9 @@ tests = ['recv_dedup', 'recv_dedup_encrypted_zvol', 'rsend_001_pos', 'send_encrypted_freeobjects', 'send_encrypted_hierarchy', 'send_encrypted_props', 'send_encrypted_truncated_files', 'send_freeobjects', 'send_realloc_files', 'send_realloc_encrypted_files', - 'send_spill_block', 'send_holds', 'send_hole_birth', 'send_mixed_raw', - 'send-wR_encrypted_zvol', 'send_partial_dataset', 'send_invalid', + 'send_spill_block', 'send_split_large_block', 'send_holds', + 'send_hole_birth', 'send_mixed_raw', 'send-wR_encrypted_zvol', + 'send_partial_dataset', 'send_invalid', 'send_large_blocks_incremental', 'send_large_blocks_initial', 'send_large_microzap_incremental', 'send_large_microzap_transitive', 'send_doall', 'send_raw_spill_block', 'send_raw_ashift', @@ -1078,8 +1073,9 @@ tests = [ 'userquota_004_pos', 'userquota_005_neg', 'userquota_006_pos', 'userquota_007_pos', 'userquota_008_pos', 'userquota_009_pos', 'userquota_010_pos', 'userquota_011_pos', 'userquota_012_neg', + 'groupspace_005_pos', 'userspace_001_pos', 'userspace_002_pos', 'userspace_004_pos', - 'userspace_encrypted', 'userspace_send_encrypted', + 'userspace_005_pos', 'userspace_encrypted', 'userspace_send_encrypted', 'userspace_encrypted_13709'] tags = ['functional', 'userquota'] diff --git a/sys/contrib/openzfs/tests/runfiles/linux.run b/sys/contrib/openzfs/tests/runfiles/linux.run index 2717bf53d0b1..2e2fb4575853 100644 --- a/sys/contrib/openzfs/tests/runfiles/linux.run +++ b/sys/contrib/openzfs/tests/runfiles/linux.run @@ -23,7 +23,8 @@ failsafe = callbacks/zfs_failsafe tags = ['functional'] [tests/functional/acl/posix:Linux] -tests = ['posix_001_pos', 'posix_002_pos', 'posix_003_pos', 'posix_004_pos'] +tests = ['posix_001_pos', 'posix_002_pos', 'posix_003_pos', + 'posix_004_pos', 'posix_005_pos'] tags = ['functional', 'acl', 'posix'] [tests/functional/acl/posix-sa:Linux] @@ -74,7 +75,8 @@ tags = ['functional', 'cli_root', 'zpool_add'] [tests/functional/cli_root/zpool_expand:Linux] tests = ['zpool_expand_001_pos', 'zpool_expand_002_pos', - 'zpool_expand_003_neg', 'zpool_expand_004_pos', 'zpool_expand_005_pos'] + 'zpool_expand_003_neg', 'zpool_expand_004_pos', 'zpool_expand_005_pos', + 'zpool_expand_006_pos'] tags = ['functional', 'cli_root', 'zpool_expand'] [tests/functional/cli_root/zpool_import:Linux] @@ -98,6 +100,11 @@ tags = ['functional', 'cli_root', 'zpool_split'] tests = ['compress_004_pos'] tags = ['functional', 'compression'] +[tests/functional/device_access:Linux] +tests = ['device_access_create', 'device_access_attach', 'device_access_add', + 'device_access_import'] +tags = ['functional', 'device_access'] + [tests/functional/devices:Linux] tests = ['devices_001_pos', 'devices_002_neg', 'devices_003_pos'] tags = ['functional', 'devices'] @@ -115,7 +122,7 @@ tests = ['events_001_pos', 'events_002_pos', 'zed_rc_filter', 'zed_fd_spill', tags = ['functional', 'events'] [tests/functional/fallocate:Linux] -tests = ['fallocate_prealloc', 'fallocate_zero-range'] +tests = ['fallocate_extend_timestamps', 'fallocate_prealloc', 'fallocate_zero-range'] tags = ['functional', 'fallocate'] [tests/functional/fault:Linux] @@ -162,6 +169,7 @@ tags = ['functional', 'mmap'] [tests/functional/mmp:Linux] tests = ['mmp_on_thread', 'mmp_on_uberblocks', 'mmp_on_off', 'mmp_interval', 'mmp_active_import', 'mmp_inactive_import', 'mmp_exported_import', + 'mmp_degraded_import', 'mmp_zhack_reclaim', 'mmp_write_uberblocks', 'mmp_reset_interval', 'multihost_history', 'mmp_on_zdb', 'mmp_write_distribution', 'mmp_hostid', 'mmp_write_slow_disk', 'mmp_concurrent_import'] @@ -229,7 +237,8 @@ tags = ['functional', 'upgrade'] [tests/functional/user_namespace:Linux] tests = ['user_namespace_001', 'user_namespace_002', 'user_namespace_003', - 'user_namespace_004'] + 'user_namespace_004', 'user_namespace_secpolicy_sys_config', + 'user_namespace_secpolicy_zinject'] tags = ['functional', 'user_namespace'] [tests/functional/userquota:Linux] diff --git a/sys/contrib/openzfs/tests/test-runner/bin/zts-report.py.in b/sys/contrib/openzfs/tests/test-runner/bin/zts-report.py.in index cef9b4db6d43..8e33abcdf542 100755 --- a/sys/contrib/openzfs/tests/test-runner/bin/zts-report.py.in +++ b/sys/contrib/openzfs/tests/test-runner/bin/zts-report.py.in @@ -222,10 +222,6 @@ maybe = { 'cli_root/zpool_destroy/zpool_destroy_001_pos': ['SKIP', 6145], 'cli_root/zpool_import/import_devices_missing': ['FAIL', 16669], 'cli_root/zpool_import/zpool_import_missing_003_pos': ['SKIP', 6839], - 'cli_root/zpool_initialize/zpool_initialize_import_export': - ['FAIL', 11948], - 'cli_root/zpool_initialize/zpool_initialize_suspend_resume': - ['FAIL', known_reason], 'cli_root/zpool_labelclear/zpool_labelclear_removed': ['FAIL', known_reason], 'cli_root/zpool_trim/setup': ['SKIP', trim_reason], diff --git a/sys/contrib/openzfs/tests/test-runner/include/logapi.shlib b/sys/contrib/openzfs/tests/test-runner/include/logapi.shlib index 29e0c7f1c9ca..d03855a1f927 100644 --- a/sys/contrib/openzfs/tests/test-runner/include/logapi.shlib +++ b/sys/contrib/openzfs/tests/test-runner/include/logapi.shlib @@ -209,12 +209,24 @@ function log_neg_expect "$@" 2>$logfile typeset status=$? + # + # A command which printed what was expected of it plainly ran, so + # its exit status is its own to interpret. Some commands report a + # count there: fio exits with the number of jobs which failed, and + # a run of 127 failing jobs is indistinguishable from a missing + # binary by status alone. + # + typeset expect_missing=1 + if [[ -n $expect ]] && grep -qi "$expect" $logfile; then + expect_missing=0 + fi + # unexpected status if (( $status == EXIT_SUCCESS )); then cat $logfile >&2 _printerror "$@" "unexpectedly exited $status" # missing binary - elif (( $status == EXIT_NOTFOUND )); then + elif (( $status == EXIT_NOTFOUND && expect_missing )); then cat $logfile >&2 _printerror "$@" "unexpectedly exited $status (File not found)" # bus error - core dump diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/ctime.c b/sys/contrib/openzfs/tests/zfs-tests/cmd/ctime.c index e1cf13130368..96ae3328de31 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/cmd/ctime.c +++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/ctime.c @@ -369,7 +369,7 @@ main(void) * a little slack in case of scheduling delays or similar. */ long delta = (long)t2 - (long)t1; - if (delta < 2 || delta > 4) { + if (delta < 2 || delta > 10) { (void) fprintf(stderr, "%s: BAD time change: t1(%ld), t2(%ld)\n", timetest_table[i].name, (long)t1, (long)t2); diff --git a/sys/contrib/openzfs/tests/zfs-tests/include/commands.cfg b/sys/contrib/openzfs/tests/zfs-tests/include/commands.cfg index 2ac7d62bb7e5..0a173667deac 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/include/commands.cfg +++ b/sys/contrib/openzfs/tests/zfs-tests/include/commands.cfg @@ -14,6 +14,7 @@ export SYSTEM_FILES_COMMON='awk bunzip2 bzcat cat + capsh chgrp chmod chown diff --git a/sys/contrib/openzfs/tests/zfs-tests/include/tunables.cfg b/sys/contrib/openzfs/tests/zfs-tests/include/tunables.cfg index e97b920f5130..cace1e0989c3 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/include/tunables.cfg +++ b/sys/contrib/openzfs/tests/zfs-tests/include/tunables.cfg @@ -39,6 +39,7 @@ DEADMAN_EVENTS_PER_SECOND deadman_events_per_second zfs_deadman_events_per_secon DEADMAN_FAILMODE deadman.failmode zfs_deadman_failmode DEADMAN_SYNCTIME_MS deadman.synctime_ms zfs_deadman_synctime_ms DEADMAN_ZIOTIME_MS deadman.ziotime_ms zfs_deadman_ziotime_ms +DIO_WRITE_VERIFY_EVENTS_PER_SECOND dio_write_verify_events_per_second zfs_dio_write_verify_events_per_second DISABLE_IVSET_GUID_CHECK disable_ivset_guid_check zfs_disable_ivset_guid_check DMU_OFFSET_NEXT_SYNC dmu_offset_next_sync zfs_dmu_offset_next_sync EMBEDDED_SLOG_MIN_MS embedded_slog_min_ms zfs_embedded_slog_min_ms diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/Makefile.am b/sys/contrib/openzfs/tests/zfs-tests/tests/Makefile.am index 1fdf014bda22..f29ba9660662 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/Makefile.am +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/Makefile.am @@ -272,6 +272,7 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \ functional/deadman/deadman.cfg \ functional/delegate/delegate.cfg \ functional/delegate/delegate_common.kshlib \ + functional/device_access/device_access.kshlib \ functional/devices/devices.cfg \ functional/devices/devices_common.kshlib \ functional/direct/dio.cfg \ @@ -412,6 +413,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/acl/posix/posix_002_pos.ksh \ functional/acl/posix/posix_003_pos.ksh \ functional/acl/posix/posix_004_pos.ksh \ + functional/acl/posix/posix_005_pos.ksh \ functional/acl/posix-sa/cleanup.ksh \ functional/acl/posix-sa/posix_001_pos.ksh \ functional/acl/posix-sa/posix_002_pos.ksh \ @@ -843,6 +845,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_root/zfs_receive/zfs_receive_-e.ksh \ functional/cli_root/zfs_receive/zfs_receive_from_encrypted.ksh \ functional/cli_root/zfs_receive/zfs_receive_from_zstd.ksh \ + functional/cli_root/zfs_receive/zfs_receive-x_props_alternation.ksh \ functional/cli_root/zfs_receive/zfs_receive_new_props.ksh \ functional/cli_root/zfs_receive/zfs_receive_raw_-d.ksh \ functional/cli_root/zfs_receive/zfs_receive_raw_incremental.ksh \ @@ -901,9 +904,6 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_root/zfs_send/zfs_send_raw.ksh \ functional/cli_root/zfs_send/zfs_send_skip_missing.ksh \ functional/cli_root/zfs_send/zfs_send_sparse.ksh \ - functional/cli_root/zfs_send_delegation/cleanup.ksh \ - functional/cli_root/zfs_send_delegation/setup.ksh \ - functional/cli_root/zfs_send_delegation/zfs_send_test.ksh \ functional/cli_root/zfs_set/cache_001_pos.ksh \ functional/cli_root/zfs_set/cache_002_neg.ksh \ functional/cli_root/zfs_set/canmount_001_pos.ksh \ @@ -1117,12 +1117,14 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_root/zpool_expand/zpool_expand_003_neg.ksh \ functional/cli_root/zpool_expand/zpool_expand_004_pos.ksh \ functional/cli_root/zpool_expand/zpool_expand_005_pos.ksh \ + functional/cli_root/zpool_expand/zpool_expand_006_pos.ksh \ functional/cli_root/zpool_export/cleanup.ksh \ functional/cli_root/zpool_export/setup.ksh \ functional/cli_root/zpool_export/zpool_export_001_pos.ksh \ functional/cli_root/zpool_export/zpool_export_002_pos.ksh \ functional/cli_root/zpool_export/zpool_export_003_neg.ksh \ functional/cli_root/zpool_export/zpool_export_004_pos.ksh \ + functional/cli_root/zpool_export/zpool_export_005_neg.ksh \ functional/cli_root/zpool_export/zpool_export_parallel_admin.ksh \ functional/cli_root/zpool_export/zpool_export_parallel_pos.ksh \ functional/cli_root/zpool_get/cleanup.ksh \ @@ -1306,6 +1308,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_root/zpool_status/zpool_status_006_pos.ksh \ functional/cli_root/zpool_status/zpool_status_007_pos.ksh \ functional/cli_root/zpool_status/zpool_status_008_pos.ksh \ + functional/cli_root/zpool_status/zpool_status_009_pos.ksh \ functional/cli_root/zpool_status/zpool_status_features_001_pos.ksh \ functional/cli_root/zpool_sync/cleanup.ksh \ functional/cli_root/zpool_sync/setup.ksh \ @@ -1429,9 +1432,6 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_user/zfs_list/zfs_list_005_neg.ksh \ functional/cli_user/zfs_list/zfs_list_007_pos.ksh \ functional/cli_user/zfs_list/zfs_list_008_neg.ksh \ - functional/cli_user/zfs_send_delegation_user/cleanup.ksh \ - functional/cli_user/zfs_send_delegation_user/setup.ksh \ - functional/cli_user/zfs_send_delegation_user/zfs_send_usertest.ksh \ functional/cli_user/zpool_iostat/cleanup.ksh \ functional/cli_user/zpool_iostat/setup.ksh \ functional/cli_user/zpool_iostat/zpool_iostat_001_neg.ksh \ @@ -1507,6 +1507,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/delegate/zfs_allow_010_pos.ksh \ functional/delegate/zfs_allow_011_neg.ksh \ functional/delegate/zfs_allow_012_neg.ksh \ + functional/delegate/zfs_allow_send.ksh \ functional/delegate/zfs_unallow_001_pos.ksh \ functional/delegate/zfs_unallow_002_pos.ksh \ functional/delegate/zfs_unallow_003_pos.ksh \ @@ -1515,6 +1516,12 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/delegate/zfs_unallow_006_pos.ksh \ functional/delegate/zfs_unallow_007_neg.ksh \ functional/delegate/zfs_unallow_008_neg.ksh \ + functional/device_access/cleanup.ksh \ + functional/device_access/setup.ksh \ + functional/device_access/device_access_create.ksh \ + functional/device_access/device_access_attach.ksh \ + functional/device_access/device_access_add.ksh \ + functional/device_access/device_access_import.ksh \ functional/devices/cleanup.ksh \ functional/devices/devices_001_pos.ksh \ functional/devices/devices_002_neg.ksh \ @@ -1583,6 +1590,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/failmode/failmode_syncalways_continue.ksh \ functional/failmode/setup.ksh \ functional/fallocate/cleanup.ksh \ + functional/fallocate/fallocate_extend_timestamps.ksh \ functional/fallocate/fallocate_prealloc.ksh \ functional/fallocate/fallocate_punch-hole.ksh \ functional/fallocate/fallocate_zero-range.ksh \ @@ -1725,6 +1733,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/mmp/cleanup.ksh \ functional/mmp/mmp_active_import.ksh \ functional/mmp/mmp_concurrent_import.ksh \ + functional/mmp/mmp_degraded_import.ksh \ functional/mmp/mmp_exported_import.ksh \ functional/mmp/mmp_hostid.ksh \ functional/mmp/mmp_inactive_import.ksh \ @@ -1737,6 +1746,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/mmp/mmp_write_distribution.ksh \ functional/mmp/mmp_write_slow_disk.ksh \ functional/mmp/mmp_write_uberblocks.ksh \ + functional/mmp/mmp_zhack_reclaim.ksh \ functional/mmp/multihost_history.ksh \ functional/mmp/setup.ksh \ functional/mount/cleanup.ksh \ @@ -1846,6 +1856,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/projectquota/projectspace_003_pos.ksh \ functional/projectquota/projectspace_004_pos.ksh \ functional/projectquota/projectspace_005_pos.ksh \ + functional/projectquota/projectspace_006_pos.ksh \ functional/projectquota/projecttree_001_pos.ksh \ functional/projectquota/projecttree_002_pos.ksh \ functional/projectquota/projecttree_003_neg.ksh \ @@ -2090,6 +2101,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/rsend/send_realloc_encrypted_files.ksh \ functional/rsend/send_realloc_files.ksh \ functional/rsend/send_spill_block.ksh \ + functional/rsend/send_split_large_block.ksh \ functional/rsend/send-wR_encrypted_zvol.ksh \ functional/rsend/setup.ksh \ functional/scrub_mirror/cleanup.ksh \ @@ -2191,11 +2203,14 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/user_namespace/user_namespace_002.ksh \ functional/user_namespace/user_namespace_003.ksh \ functional/user_namespace/user_namespace_004.ksh \ + functional/user_namespace/user_namespace_secpolicy_sys_config.ksh \ + functional/user_namespace/user_namespace_secpolicy_zinject.ksh \ functional/userquota/cleanup.ksh \ functional/userquota/groupspace_001_pos.ksh \ functional/userquota/groupspace_002_pos.ksh \ functional/userquota/groupspace_003_pos.ksh \ functional/userquota/groupspace_004_pos.ksh \ + functional/userquota/groupspace_005_pos.ksh \ functional/userquota/setup.ksh \ functional/userquota/defaultuserquota_001_pos.ksh \ functional/userquota/defaultuserquota_002_pos.ksh \ @@ -2227,6 +2242,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/userquota/userspace_002_pos.ksh \ functional/userquota/userspace_003_pos.ksh \ functional/userquota/userspace_004_pos.ksh \ + functional/userquota/userspace_005_pos.ksh \ functional/userquota/userspace_encrypted.ksh \ functional/userquota/userspace_send_encrypted.ksh \ functional/userquota/userspace_encrypted_13709.ksh \ diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/acl_common.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/acl_common.kshlib index 889c911e1b19..f0a094f048f6 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/acl_common.kshlib +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/acl_common.kshlib @@ -165,6 +165,7 @@ function cleanup if [[ -d $TESTDIR ]]; then cd $TESTDIR rm -rf $TESTDIR/* + log_must setfacl -b $TESTDIR fi } diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/posix/posix_004_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/posix/posix_004_pos.ksh index ba96b02f53f9..2846d1dfb252 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/posix/posix_004_pos.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/posix/posix_004_pos.ksh @@ -41,6 +41,7 @@ verify_runnable "both" log_assert "Verify chown works with POSIX ACLs" +log_onexit cleanup log_must setfacl -d -m u:$ZFS_ACL_STAFF1:rwx $TESTDIR log_must setfacl -b $TESTDIR diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/posix/posix_005_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/posix/posix_005_pos.ksh new file mode 100755 index 000000000000..ffb500cd2412 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/acl/posix/posix_005_pos.ksh @@ -0,0 +1,71 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the License at usr/src/OPENSOLARIS.LICENSE. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# +# CDDL HEADER END +# +# +# DESCRIPTION: +# Verify that online rollback invalidates cached POSIX ACLs. +# +# STRATEGY: +# 1. Snapshot a file whose group mode bit requires an ACL check, but where +# the test user has no access. +# 2. Grant the test user read access so the VFS caches the newer ACL. +# 3. Keep the inode active over rollback, then verify the test user cannot +# read the recovered file without dropping inode or page caches. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/acl/acl_common.kshlib + +verify_runnable "both" +log_assert "Online rollback invalidates cached POSIX ACLs" + +typeset file=$TESTDIR/posix-acl-rollback +typeset snap=$TESTPOOL/$TESTFS@posix-acl-rollback +typeset user=$ZFS_ACL_STAFF1 + +function cleanup +{ + snapexists $snap && destroy_dataset $snap -f + rm -f $file +} + +log_onexit cleanup + +log_must touch $file +# Keep a group read bit so generic_permission() consults a cached POSIX ACL. +# The test user is not in root's group, so this still denies the initial read. +log_must chmod 640 $file +log_mustnot user_run $user cat $file +log_must zfs snapshot $snap + +# setfacl updates the VFS ACL cache before rollback. +log_must setfacl -m u:$user:r-- $file +log_must user_run $user cat $file + +# Online rollback shrinks the dcache. Hold the inode alive so the following +# pathname lookup reuses its VFS ACL cache instead of allocating a new inode. +log_must eval "exec 9< $file" +log_must zfs rollback $snap +log_mustnot user_run $user cat $file +log_must eval "exec 9>&-" + +log_pass "Online rollback invalidates cached POSIX ACLs" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive-x_props_alternation.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive-x_props_alternation.ksh new file mode 100755 index 000000000000..f099519da3e2 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive-x_props_alternation.ksh @@ -0,0 +1,80 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by . All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib + +# +# DESCRIPTION: +# Verify 'zfs receive -x' consistently excludes a property across +# incremental sends with -p, and does not alternate. +# +# STRATEGY: +# 1. Create source and destination datasets. +# 2. Set a native property (refquota) on source. +# 3. Full send with -p, receive with -x refquota. +# 4. Verify refquota is not set on destination. +# 5. Loop several incremental sends/receives with -p/-x. +# 6. Each iteration verify refquota stays consistently excluded. +# + +verify_runnable "global" + +function cleanup +{ + destroy_dataset "$orig" "-rf" + destroy_dataset "$dest" "-rf" +} + +log_assert "'zfs receive -x' consistently excludes properties" \ + "across incremental sends." +log_onexit cleanup + +orig=$TESTPOOL/$TESTFS1 +dest=$TESTPOOL/$TESTFS2 + +# setup source and destination +log_must zfs create $orig +log_must zfs create $dest + +# set a native property on source +log_must zfs set refquota=64M $orig +log_must check_prop_source $orig refquota 67108864 local + +# full send with -p, receive with -x +log_must zfs snapshot $orig@snap1 +log_must eval "zfs send -p $orig@snap1 | zfs receive -e -x refquota $dest" + +# destination should NOT have refquota from the stream +log_must [ "$(get_prop refquota $dest/$TESTFS1)" = "0" ] + +# incremental sends: the excluded property must never appear on dest +typeset -i i=2 +while (( i <= 4 )); do + log_must zfs snapshot $orig@snap$i + log_must eval "zfs send -p -i $orig@snap$((i-1)) $orig@snap$i \ + | zfs receive -e -F -x refquota $dest" + log_must [ "$(get_prop refquota $dest/$TESTFS1)" = "0" ] + (( i = i + 1 )) +done + +log_pass "'zfs receive -x' consistently excludes properties" \ + "across incremental sends." diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/cleanup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/cleanup.ksh deleted file mode 100755 index 4a59e15cc693..000000000000 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/cleanup.ksh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/ksh -p -# SPDX-License-Identifier: CDDL-1.0 -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or https://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2025, Klara Inc. -# - -. $STF_SUITE/include/libtest.shlib -. $STF_SUITE/tests/functional/delegate/delegate_common.kshlib - - -poolexists $TESTPOOL1 && \ - destroy_pool $TESTPOOL1 - -del_user $STAFF1 -del_user $STAFF2 -del_group $STAFF_GROUP - -del_user $OTHER1 -del_user $OTHER2 -del_group $OTHER_GROUP - -default_cleanup diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/setup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/setup.ksh deleted file mode 100755 index 0978193eddc4..000000000000 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/setup.ksh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/ksh -p -# SPDX-License-Identifier: CDDL-1.0 -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or https://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2025, Klara Inc. -# - -. $STF_SUITE/include/libtest.shlib -. $STF_SUITE/tests/functional/delegate/delegate_common.kshlib - -# Create staff group and add two user to it -log_must add_group $STAFF_GROUP -if ! id $STAFF1 > /dev/null 2>&1; then - log_must add_user $STAFF_GROUP $STAFF1 -fi -if ! id $STAFF2 > /dev/null 2>&1; then - log_must add_user $STAFF_GROUP $STAFF2 -fi - -# Create other group and add two user to it -log_must add_group $OTHER_GROUP -if ! id $OTHER1 > /dev/null 2>&1; then - log_must add_user $OTHER_GROUP $OTHER1 -fi -if ! id $OTHER2 > /dev/null 2>&1; then - log_must add_user $OTHER_GROUP $OTHER2 -fi -DISK=${DISKS%% *} - -default_raidz_setup $DISKS diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/zfs_send_test.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/zfs_send_test.ksh deleted file mode 100755 index d018f313fae1..000000000000 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zfs_send_delegation/zfs_send_test.ksh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/ksh -p -# SPDX-License-Identifier: CDDL-1.0 -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or https://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2025, Klara Inc. -# - -# STRATEGY: -# 1. Create a pool (this is done by the test framework) -# 2. Create an encrypted dataset -# 3. Write random data to the encrypted dataset -# 4. Snapshot the dataset -# 5. As root: attempt a send and raw send (both should succeed) -# 6. Create a delegation (zfs allow -u user send testpool/encrypted_dataset) -# 7. As root: attempt a send and raw send (both should succeed) -# 8. Create a delegation (zfs allow -u user send:raw testpool/encrypted_dataset) -# 9. As root: attempt a send and raw send (both should succeed) -# 10. Disable delegation (zfs unallow) -# 11. As root: attempt a send and raw send (both should succeed) -# 12. Clean up (handled by framework) -# -# Tested as a user under ../cli_user/zfs_send_delegation_user/ - -. $STF_SUITE/include/libtest.shlib -. $STF_SUITE/tests/functional/cli_root/zfs_create/zfs_create_common.kshlib -. $STF_SUITE/tests/functional/cli_root/zfs_create/properties.kshlib -. $STF_SUITE/tests/functional/cli_root/zfs_load-key/zfs_load-key_common.kshlib -. $STF_SUITE/tests/functional/delegate/delegate.cfg - -# create encrypted dataset - -log_must eval "echo $PASSPHRASE | zfs create -o encryption=on -o keyformat=passphrase $TESTPOOL/$TESTFS1" - -# create target dataset for receives -if ! zfs list | grep testfs2 >/dev/null 2>&1; then - dataset_created="TRUE" - log_must zfs create $TESTPOOL/$TESTFS2 -fi - -# create user and group -typeset perms="snapshot,reservation,compression,checksum,userprop,receive" - -log_note "Added permissions to the $OTHER1 user." -log_must zfs allow $OTHER1 $perms $TESTPOOL/$TESTFS1 -log_must zfs allow $OTHER1 $perms $TESTPOOL/$TESTFS2 - -# create random data -log_must fill_fs $TESTPOOL/$TESTFS1/child 1 2047 1024 1 R - -# snapshot -log_must zfs snapshot $TESTPOOL/$TESTFS1@snap1 - - -# check baseline send abilities (should pass) -log_must eval "zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv0_datastream.$$" -log_must eval "zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv0raw_datastream.$$" - - -# create delegation -log_must zfs allow $OTHER1 send $TESTPOOL/$TESTFS1 - -# attempt send with full allow - -log_must eval "zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv1_datastream.$$" -log_must eval "zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv1raw_datastream.$$" - -# create raw delegation -log_must zfs allow $OTHER1 send:raw $TESTPOOL/$TESTFS1 -log_must zfs unallow $OTHER1 send $TESTPOOL/$TESTFS1 - -# test new send abilities (should pass) -log_must eval "zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv2_datastream.$$" -log_must eval "zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv2raw_datastream.$$" - - -# disable raw delegation -zfs unallow $OTHER1 send:raw $TESTPOOL/$TESTFS1 -zfs allow $OTHER1 send $TESTPOOL/$TESTFS1 - -# verify original send abilities (should pass) -log_must eval "zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv3_datastream.$$" -log_must eval "zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive $TESTPOOL/$TESTFS2/zfsrecv3raw_datastream.$$" - - -function cleanup -{ - datasetexists $TESTPOOL/$TESTFS1 && \ - destroy_dataset $TESTPOOL/$TESTFS1 -r \ - destroy_dataset $TESTPOOL/$TESTFS2 -r - -} diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_expand/zpool_expand_006_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_expand/zpool_expand_006_pos.ksh new file mode 100755 index 000000000000..f82eea2c12ca --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_expand/zpool_expand_006_pos.ksh @@ -0,0 +1,103 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Andrew Mochalskyi. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/cli_root/zpool_expand/zpool_expand.cfg + +# +# DESCRIPTION: +# A pool on a whole-disk vdev autoexpands from the kernel's own +# capacity change event alone (issue #12505). +# +# STRATEGY: +# 1) Create a pool with autoexpand=on on a single scsi_debug device, +# so zfs owns the whole disk and puts a partition table on it +# 2) Grow the disk and let the SCSI layer pick up the new capacity +# 3) Wait for the pool to expand +# +# The kernel reports a resize with a single change event on the disk; +# nothing is generated for the partitions. zpool_expand_001_pos +# passes this point on an unpatched zed only because +# block_device_wait runs a bare udevadm trigger, which resends change +# events for the zfs_member partitions and hands zed the vdev guid +# the resize never delivered. For the same reason the udev events +# left over from pool creation must be drained before growing the +# disk: only udevadm settle may be used after that point, so that zed +# sees the resize event and nothing else, just like with a disk grown +# in production. +# + +verify_runnable "global" + +function cleanup +{ + poolexists $TESTPOOL1 && destroy_pool $TESTPOOL1 + unload_scsi_debug +} + +# Wait for the pool size to grow past $1. +function wait_for_autoexpand +{ + typeset prev_size=$1 + typeset -i i + + for (( i = 0; i < 60; i++ )); do + typeset new_size=$(get_pool_prop size $TESTPOOL1) + if [[ $new_size -gt $prev_size ]]; then + return + fi + sleep 1 + done + log_fail "$TESTPOOL1 never grew past $prev_size (still $new_size)" +} + +log_onexit cleanup + +log_assert "whole-disk pool autoexpands from the disk's own resize event" + +MINVDEVSIZE_MB=$((MINVDEVSIZE / 1048576)) +load_scsi_debug $MINVDEVSIZE_MB 1 1 1 '512b' +block_device_wait +DEV=$(get_debug_device) + +log_must zpool create -o autoexpand=on $TESTPOOL1 $DEV + +# Let the udev fallout of the pool creation (partition table writes, +# device node closes) fully play out before the resize, so the only +# event left for zed to see below is the resize itself. +udevadm settle +sleep 2 +udevadm settle + +typeset prev_size=$(get_pool_prop size $TESTPOOL1) + +# Grow the device and have the SCSI disk driver re-read the capacity. +# This makes the kernel emit a change event with RESIZE=1 for the +# disk, and nothing for its partitions. +echo "2" > /sys/bus/pseudo/drivers/scsi_debug/virtual_gb +echo "1" > /sys/class/block/$DEV/device/rescan +udevadm settle + +wait_for_autoexpand $prev_size + +log_note "$TESTPOOL1 grew from $prev_size to $(get_pool_prop size $TESTPOOL1)" + +log_pass "whole-disk pool autoexpanded from the disk's own resize event" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_export/zpool_export_005_neg.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_export/zpool_export_005_neg.ksh new file mode 100755 index 000000000000..2978f8c6a28d --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_export/zpool_export_005_neg.ksh @@ -0,0 +1,89 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2025 by Heonje LEE. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/cli_root/zpool_export/zpool_export.kshlib + +# +# DESCRIPTION: +# Exporting a pool with a mounted zvol block device must fail with +# EBUSY rather than hanging the system in an uninterruptible sleep. +# This mirrors the behavior already provided for mounted ZFS +# filesystems (see zpool_export_002_pos.ksh). +# +# STRATEGY: +# 1. Create a pool with a zvol. +# 2. Format and mount the zvol block device. +# 3. Attempt to export the pool -- must fail (EBUSY). +# 4. Unmount the zvol. +# 5. Export should now succeed. +# + +verify_runnable "global" + +function cleanup +{ + ismounted $mntpnt $NEWFS_DEFAULT_FS && log_must umount $mntpnt + [[ -d $mntpnt ]] && log_must rmdir $mntpnt + datasetexists $TESTPOOL1/vol && log_must zfs destroy $TESTPOOL1/vol + poolexists $TESTPOOL1 && destroy_pool $TESTPOOL1 + [[ -f $vdev ]] && log_must rm -f $vdev + zpool_export_cleanup +} + +mntpnt=$TESTDIR0/zvol_export_mnt +vdev=$TESTDIR0/zvol_export_vdev + +log_onexit cleanup + +log_assert "Exporting a pool with a mounted zvol returns EBUSY" + +log_must mkdir -p $TESTDIR0 +log_must truncate -s $MINVDEVSIZE $vdev + +# 1. Create pool + zvol +log_must zpool create $TESTPOOL1 $vdev +log_must zfs create -V 100M $TESTPOOL1/vol +block_device_wait ${ZVOL_DEVDIR}/$TESTPOOL1/vol + +# 2. Format and mount the zvol block device +log_must eval "new_fs ${ZVOL_RDEVDIR}/$TESTPOOL1/vol >/dev/null 2>&1" +log_must mkdir -p $mntpnt +log_must mount ${ZVOL_DEVDIR}/$TESTPOOL1/vol $mntpnt + +# 3. Export must fail with EBUSY (not hang) +log_mustnot zpool export $TESTPOOL1 +log_must poolexists $TESTPOOL1 + +# 4. Unmount the zvol +log_must umount $mntpnt + +# 5. Export should now succeed +log_must zpool export $TESTPOOL1 +log_mustnot poolexists $TESTPOOL1 + +log_pass "Export with mounted zvol correctly returned EBUSY" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_import_export.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_import_export.ksh index 341f4f75cf7d..6f93872a0c1b 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_import_export.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_import_export.ksh @@ -44,6 +44,25 @@ DISK1=${DISKS%% *} +function cleanup +{ + # Destroy the pool (stopping the initialize thread) before restoring + # the chunk size, so the running thread never issues a write larger + # than the buffer it allocated at the smaller size. + if poolexists $TESTPOOL; then + log_must zpool destroy -f $TESTPOOL + fi + [[ "$default_chunk_sz" ]] && \ + log_must set_tunable64 INITIALIZE_CHUNK_SIZE $default_chunk_sz +} +log_onexit cleanup + +# Make initializing slow enough that it is still running after the pool has +# been exported and re-imported below, rather than racing to completion on a +# small or fast vdev (see zpool_wait_initialize_*). +default_chunk_sz=$(get_tunable INITIALIZE_CHUNK_SIZE) +log_must set_tunable64 INITIALIZE_CHUNK_SIZE 512 + log_must zpool create -f $TESTPOOL $DISK1 log_must zpool initialize $TESTPOOL diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_multiple_pools.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_multiple_pools.ksh index cc7bca5445d2..0ca01c49577e 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_multiple_pools.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_multiple_pools.ksh @@ -42,16 +42,24 @@ verify_runnable "global" cleanup() { for pool in {1..4}; do - zpool destroy $TESTPOOL${pool} + poolexists $TESTPOOL${pool} && destroy_pool $TESTPOOL${pool} rm -rf $TESTDIR${pool} done rm -f $DISK1 $DISK2 $DISK3 $DISK4 + + # Restore the chunk size only after the pools (and their initialize + # threads) are gone, so a still-running thread never sees a larger value + # than the fill buffer it allocated at the smaller size. + [[ -n "$default_chunk_size" ]] && \ + log_must set_tunable64 INITIALIZE_CHUNK_SIZE $default_chunk_size } log_onexit cleanup log_assert "Verify if 'zpool initialize -a' works correctly with multiple pools." +default_chunk_size=$(get_tunable INITIALIZE_CHUNK_SIZE) + DEVSIZE='5G' TESTDIR="$TEST_BASE_DIR/zpool_initialize_multiple_pools" DISK1="$TEST_BASE_DIR/zpool_disk1.dat" @@ -108,6 +116,16 @@ if [[ $complete_count -ne 4 ]]; then log_fail "Expected 4 pools to have initialize status 'uninitialized', but found ${complete_count}." fi +# The suspend check below requires initializing to still be running on every +# pool. At the default chunk size a pool can finish initializing before +# "zpool initialize -a -s" runs, so it would report "completed" instead of +# "suspended" and the test would fail intermittently. Throttle the chunk +# size (as the zpool_wait_initialize_* tests do) so initializing stays active +# through the suspend and cancel checks. The earlier completion phases are +# deliberately left at the default size so "zpool wait" and "-w" return +# promptly. +log_must set_tunable64 INITIALIZE_CHUNK_SIZE 512 + log_must zpool initialize -a for pool in {1..4}; do diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_suspend_resume.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_suspend_resume.ksh index 79573daa20fb..98bc9037b96e 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_suspend_resume.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_initialize/zpool_initialize_suspend_resume.ksh @@ -42,6 +42,25 @@ DISK1=${DISKS%% *} +function cleanup +{ + # Destroy the pool (stopping the initialize thread) before restoring + # the chunk size, so the running thread never issues a write larger + # than the buffer it allocated at the smaller size. + if poolexists $TESTPOOL; then + log_must zpool destroy -f $TESTPOOL + fi + [[ "$default_chunk_sz" ]] && \ + log_must set_tunable64 INITIALIZE_CHUNK_SIZE $default_chunk_sz +} +log_onexit cleanup + +# Make initializing slow enough that it does not race to completion before it +# can be suspended and observed, rather than finishing on a small or fast vdev +# (see zpool_wait_initialize_*). +default_chunk_sz=$(get_tunable INITIALIZE_CHUNK_SIZE) +log_must set_tunable64 INITIALIZE_CHUNK_SIZE 512 + log_must zpool create -f $TESTPOOL $DISK1 log_must zpool initialize $TESTPOOL diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_iostat/zpool_iostat_interval_all.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_iostat/zpool_iostat_interval_all.ksh index 8e040058ec3e..5ce01aab7220 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_iostat/zpool_iostat_interval_all.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_iostat/zpool_iostat_interval_all.ksh @@ -60,8 +60,19 @@ expect_iostat "POOLBOTH" log_must zpool create pool2 $vdev2 delay_iostat +# Export the pools one at a time rather than with a single "zpool export -a". +# iostat samples every 0.1s and "export -a" tears the pools down +# sequentially, so it would sometimes catch the intermediate one-pool state +# and emit an extra chunk that is not in the expected output. Exporting each +# pool explicitly makes that transition deterministic, mirroring the +# one-pool-at-a-time imports below. +expect_iostat "HEADER" +expect_iostat "POOL2" +log_must zpool export pool1 +delay_iostat + expect_iostat "NOPOOL" -log_must zpool export -a +log_must zpool export pool2 delay_iostat expect_iostat "HEADER" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_009_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_009_pos.ksh new file mode 100755 index 000000000000..994963de28da --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_root/zpool_status/zpool_status_009_pos.ksh @@ -0,0 +1,89 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 + +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026 by Michael Heller. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Verify 'zpool status -L' does not truncate a long vdev path. +# +# STRATEGY: +# 1. Create a pool on a file vdev whose path is longer than the buffer the +# resolved path used to be copied into. +# 2. Verify 'zpool status -L' reports the whole path. +# 3. Verify 'zpool status' without -L reports it too, which it always did. +# + +verify_runnable "global" + +function cleanup +{ + poolexists $TESTPOOL2 && destroy_pool $TESTPOOL2 + log_must rm -rf $longdir +} + +log_assert "'zpool status -L' does not truncate a long vdev path" + +log_onexit cleanup + +# +# The resolved path has to exceed 63 bytes to reach the case this covers, so +# the name is padded out and the length checked rather than assumed. +# +longdir=$TESTDIR/$(printf 'd%.0s' {1..40}) +longvdev=$longdir/$(printf 'v%.0s' {1..40}) + +typeset -i pathlen=${#longvdev} +if (( pathlen <= 63 )); then + log_fail "vdev path is only $pathlen bytes, too short to truncate" +fi +log_note "vdev path is $pathlen bytes" + +log_must mkdir -p $longdir +log_must truncate -s $MINVDEVSIZE $longvdev +log_must zpool create -f $TESTPOOL2 $longvdev + +# +# -L resolves symlinks in the path. A file vdev resolves to itself, so the +# whole path must come back unchanged. Report whatever name was printed +# rather than only whether it matched, so a failure shows how far it was cut. +# +function vdev_name_from_status # args +{ + zpool status "$@" $TESTPOOL2 | + awk -v d="$TESTDIR" 'index($1, d) == 1 {print $1; exit}' +} + +typeset name +name=$(vdev_name_from_status -L) +if [[ "$name" != "$longvdev" ]]; then + log_fail "'zpool status -L' reported a ${#name} byte name," \ + "expected $pathlen bytes: '$name'" +fi + +name=$(vdev_name_from_status) +if [[ "$name" != "$longvdev" ]]; then + log_fail "'zpool status' reported a ${#name} byte name," \ + "expected $pathlen bytes: '$name'" +fi + +log_pass "'zpool status -L' does not truncate a long vdev path" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/cleanup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/cleanup.ksh deleted file mode 100755 index 4a59e15cc693..000000000000 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/cleanup.ksh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/ksh -p -# SPDX-License-Identifier: CDDL-1.0 -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or https://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2025, Klara Inc. -# - -. $STF_SUITE/include/libtest.shlib -. $STF_SUITE/tests/functional/delegate/delegate_common.kshlib - - -poolexists $TESTPOOL1 && \ - destroy_pool $TESTPOOL1 - -del_user $STAFF1 -del_user $STAFF2 -del_group $STAFF_GROUP - -del_user $OTHER1 -del_user $OTHER2 -del_group $OTHER_GROUP - -default_cleanup diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/setup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/setup.ksh deleted file mode 100755 index 0978193eddc4..000000000000 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/setup.ksh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/ksh -p -# SPDX-License-Identifier: CDDL-1.0 -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or https://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2025, Klara Inc. -# - -. $STF_SUITE/include/libtest.shlib -. $STF_SUITE/tests/functional/delegate/delegate_common.kshlib - -# Create staff group and add two user to it -log_must add_group $STAFF_GROUP -if ! id $STAFF1 > /dev/null 2>&1; then - log_must add_user $STAFF_GROUP $STAFF1 -fi -if ! id $STAFF2 > /dev/null 2>&1; then - log_must add_user $STAFF_GROUP $STAFF2 -fi - -# Create other group and add two user to it -log_must add_group $OTHER_GROUP -if ! id $OTHER1 > /dev/null 2>&1; then - log_must add_user $OTHER_GROUP $OTHER1 -fi -if ! id $OTHER2 > /dev/null 2>&1; then - log_must add_user $OTHER_GROUP $OTHER2 -fi -DISK=${DISKS%% *} - -default_raidz_setup $DISKS diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/zfs_send_usertest.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/zfs_send_usertest.ksh deleted file mode 100755 index ea54a962fb0a..000000000000 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/cli_user/zfs_send_delegation_user/zfs_send_usertest.ksh +++ /dev/null @@ -1,150 +0,0 @@ -#!/bin/ksh -p -# SPDX-License-Identifier: CDDL-1.0 -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or https://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2025, Klara Inc. -# - -# STRATEGY: -# 1. Create a pool (this is done by the test framework) -# 2. Create a user -# 3. Create an encrypted dataset -# 4. Write random data to the encrypted dataset -# 5. Snapshot the dataset -# 6. As root: attempt a send and raw send (both should succeed) -# 7. As user: attempt a send and raw send (both should fail, no permission) -# 8. Create a delegation (zfs allow -u user send testpool/encrypted_dataset) -# 9. As root: attempt a send and raw send (both should succeed) -# 10. As user: attempt a send and raw send (both should succeed) -# 11. Create a delegation (zfs allow -u user sendraw testpool/encrypted_dataset) -# 12. As root: attempt a send and raw send (both should succeed) -# 13. As user: attempt a send and raw send (send should fail, raw send should succeed) -# 14. Disable delegation (zfs unallow) -# 15. As root: attempt a send and raw send (both should succeed) -# 16. As user: attempt a send and raw send (both should fail, no permission) -# 17. Clean up (handled by framework) -# root tests to verify this doesnt affect root user under ../cli_root/zfs_send_delegation/ -. $STF_SUITE/include/libtest.shlib -. $STF_SUITE/tests/functional/cli_root/zfs_create/zfs_create_common.kshlib -. $STF_SUITE/tests/functional/cli_root/zfs_create/properties.kshlib -. $STF_SUITE/tests/functional/cli_root/zfs_load-key/zfs_load-key_common.kshlib -. $STF_SUITE/tests/functional/delegate/delegate.cfg - -# create encrypted dataset - -log_must eval "echo $PASSPHRASE | zfs create -o encryption=on -o keyformat=passphrase $TESTPOOL/$TESTFS1" - -# create target dataset for receives -log_must zfs create $TESTPOOL/$TESTFS2 - -# set user perms -# need to run chown for fs permissions for $OTHER1 -typeset perms="snapshot,reservation,compression,checksum,userprop,receive,mount,create" - -log_must zfs allow $OTHER1 $perms $TESTPOOL/$TESTFS1 -log_must zfs allow $OTHER1 $perms $TESTPOOL/$TESTFS2 -log_must chown ${OTHER1}:${OTHER_GROUP} /$TESTPOOL/$TESTFS2 - -# create random data -log_must fill_fs $TESTPOOL/$TESTFS1/child 1 2047 1024 1 R - -# snapshot -log_must zfs snapshot $TESTPOOL/$TESTFS1@snap1 - -# note -# we need to use `sh -c` here becuase the quoting on <<<"$*" in the user_run wrapper is broken once pipes and redirects get involved - -# check baseline send abilities (should fail) -log_mustnot user_run $OTHER1 sh -c "'zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv0_user_datastream.$$'" -# verify nothing went through -if [ -s $TESTPOOL/$TESTFS2/zfsrecv0_user_datastream.$$ ] -then - log_fail "A zfs recieve was completed in $TESTPOOL/$TESTFS2/zfsrecv0_user_datastream !" -fi -log_mustnot user_run $OTHER1 sh -c "'zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv0raw_user_datastream.$$'" -# verify nothing went through -if [ -s $TESTPOOL/$TESTFS2/zfsrecv0raw_user_datastream.$$ ] -then - log_fail "A zfs recieve was completed in $TESTPOOL/$TESTFS2/zfsrecv0raw_user_datastream !" -fi - -# create delegation -log_must zfs allow $OTHER1 send $TESTPOOL/$TESTFS1 - -# attempt send with full allow (should pass) -log_must user_run $OTHER1 sh -c "'zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv1_user_datastream.$$'" -log_must user_run $OTHER1 sh -c "'zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv1raw_user_datastream.$$'" - - -# create raw delegation -log_must zfs allow $OTHER1 send:raw $TESTPOOL/$TESTFS1 -# We have to remove 'send' to confirm 'send raw' only allows what we want -log_must zfs unallow -u $OTHER1 send $TESTPOOL/$TESTFS1 - -# test new sendraw abilities (send should fail, sendraw should pass) -log_mustnot user_run $OTHER1 sh -c "'zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv2_user_datastream.$$'" -# verify nothing went through -if [ -s $TESTPOOL/$TESTFS2/zfsrecv2_user_datastream.$$ ] -then - log_fail "A zfs recieve was completed in $TESTPOOL/$TESTFS2/zfsrecv2_user_datastream !" -fi -log_must user_run $OTHER1 sh -c "'zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv2raw_user_datastream.$$'" - -# test incremental send with intermediates (should pass) -log_must zfs allow $OTHER1 hold $TESTPOOL/$TESTFS1 -log_must zfs snapshot $TESTPOOL/$TESTFS1@snap2 -log_must user_run $OTHER1 sh -c "'zfs send -w -I $TESTPOOL/$TESTFS1@snap1 $TESTPOOL/$TESTFS1@snap2 > /dev/null'" - -# disable raw delegation -log_must zfs unallow -u $OTHER1 send:raw $TESTPOOL/$TESTFS1 -log_must zfs allow $OTHER1 send $TESTPOOL/$TESTFS1 - -# test with raw taken away (should pass) -log_must user_run $OTHER1 sh -c "'zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv3_user_datastream.$$'" -log_must user_run $OTHER1 sh -c "'zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv3raw_user_datastream.$$'" - -# disable send abilities -log_must zfs unallow -u $OTHER1 send $TESTPOOL/$TESTFS1 - -# verify original send abilities (should fail) -log_mustnot user_run $OTHER1 sh -c "'zfs send $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv4_user_datastream.$$'" -# verify nothing went through -if [ -s $TESTPOOL/$TESTFS2/zfsrecv4_user_datastream.$$ ] -then - log_fail "A zfs recieve was completed in $TESTPOOL/$TESTFS2/zfsrecv4_user_datastream !" -fi -log_mustnot user_run $OTHER1 sh -c "'zfs send -w $TESTPOOL/$TESTFS1@snap1 | zfs receive -u $TESTPOOL/$TESTFS2/zfsrecv4raw_user_datastream.$$'" -# verify nothing went through -if [ -s $TESTPOOL/$TESTFS2/zfsrecv4raw_user_datastream.$$ ] -then - log_fail "A zfs recieve was completed in $TESTPOOL/$TESTFS2/zfsrecv4raw_user_datastream !" -fi - - -function cleanup -{ - datasetexists $TESTPOOL/$TESTFS1 && \ - destroy_dataset $TESTPOOL/$TESTFS1 -r \ - destroy_dataset $TESTPOOL/$TESTFS2 -r - -} diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/delegate/delegate_common.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/delegate/delegate_common.kshlib index 345239b88680..f8c6a937adb6 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/delegate/delegate_common.kshlib +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/delegate/delegate_common.kshlib @@ -53,14 +53,23 @@ function cleanup_user_group # # Restore test file system to the original status. # -function restore_root_datasets +function restore_root_datasets # do_encrypt { + typeset do_encrypt=${1:-} + + typeset enc_args="" + typeset password="" + if [[ -n "$do_encrypt" ]] ; then + enc_args="-o encryption=on -o keyformat=passphrase -o keylocation=prompt" + password="password" + fi + destroy_dataset "$ROOT_TESTFS" "-Rf" - log_must zfs create $ROOT_TESTFS + log_must eval "echo $password | zfs create $enc_args $ROOT_TESTFS" if is_global_zone ; then destroy_dataset "$ROOT_TESTVOL" "-Rf" - log_must zfs create -V $VOLSIZE $ROOT_TESTVOL + log_must eval "echo $password | zfs create -V $VOLSIZE $enc_args $ROOT_TESTVOL" block_device_wait fi @@ -166,6 +175,9 @@ function common_perm send) verify_send $user $perm $dtst ;; + send_raw) # not a true permission + verify_send_raw $user $perm $dtst + ;; allow) verify_allow $user $perm $dtst ;; @@ -357,6 +369,40 @@ function verify_send return $ret } +# +# Verify permission send for specified user on the dataset +# $1 user +# $2 permission +# $3 dataset +# +function verify_send_raw +{ + typeset user=$1 + typeset perm=$2 + typeset dtst=$3 + + typeset oldval + typeset stamp=${perm}.${user}.$RANDOM + typeset snap=$dtst@snap.$stamp + + typeset -i ret=1 + + log_must zfs snapshot $snap + typeset bak_user=$TEST_BASE_DIR/bak.$user.$stamp + typeset bak_root=$TEST_BASE_DIR/bak.root.$stamp + + user_run $user eval "zfs send -w $snap > $bak_user" + log_must eval "zfs send -w $snap > $bak_root" + + if [ "$(cksum < $bak_user)" = "$(cksum < $bak_root)" ]; then + ret=0 + fi + + rm -rf $bak_user $bak_root + + return $ret +} + function verify_fs_receive { typeset user=$1 diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/delegate/zfs_allow_send.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/delegate/zfs_allow_send.ksh new file mode 100755 index 000000000000..6e9e78ad7c09 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/delegate/zfs_allow_send.ksh @@ -0,0 +1,57 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/tests/functional/delegate/delegate_common.kshlib + +verify_runnable "both" + +log_assert "Verify send sub-permissions correctly limit send options." +log_onexit restore_root_datasets + +for dtst in $DATASETS; do + + for enc in '' 'y' ; do + # full 'send' permission can do it all + log_must restore_root_datasets $enc + log_must zfs allow $STAFF1 "send" $dtst + log_must verify_perm $dtst "send" $STAFF1 + log_must verify_perm $dtst "send_raw" $STAFF1 + + # 'send:raw' can only do raw send + log_must restore_root_datasets $enc + log_must zfs allow $STAFF1 "send:raw" $dtst + log_must verify_noperm $dtst "send" $STAFF1 + log_must verify_perm $dtst "send_raw" $STAFF1 + done + + # 'send:encrypted' cannot do anything on unencrypted datasets + log_must restore_root_datasets + log_must zfs allow $STAFF1 "send:encrypted" $dtst + log_must verify_noperm $dtst "send" $STAFF1 + log_must verify_noperm $dtst "send_raw" $STAFF1 + + # 'send:encrypted' can do raw send only on encrypted datasets + log_must restore_root_datasets 'y' + log_must zfs allow $STAFF1 "send:encrypted" $dtst + log_must verify_noperm $dtst "send" $STAFF1 + log_must verify_perm $dtst "send_raw" $STAFF1 + +done + +log_must restore_root_datasets + +log_pass "Verify send-permissions correctly limit send options." diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/cleanup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/cleanup.ksh new file mode 100755 index 000000000000..751dee55ed91 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/cleanup.ksh @@ -0,0 +1,21 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/device_access/device_access.kshlib + +log_pass diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access.kshlib new file mode 100644 index 000000000000..548c79ff8d5f --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access.kshlib @@ -0,0 +1,68 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/include/libtest.shlib + +typeset -a _disks=($DISKS) +typeset -r DEV1=/dev/${_disks[0]} +typeset -r DEV2=/dev/${_disks[1]} +typeset -r DEV3=/dev/${_disks[2]} + +typeset -A _saved_perms + +function save_perms { # path + typeset path="$1" + _saved_perms[$path]=$(stat -c '%a' "$path") +} + +function restore_perms { #path + typeset path="$1" + if [[ -z ${_saved_perms[$path]} ]] ; then + log_fail "restore_perms($path): must call save_perms first" + fi + chmod ${_saved_perms[$path]} "$path" +} + +function check_vdevs { #pool, vdevs... + typeset pool="$1" + typeset want=$(echo "${@:2}" | xargs -n 1 | sort | xargs) + typeset have=$(zpool list -HvLP -o name | grep /dev | sort | xargs) + log_must test "$want" == "$have" +} + +function without_cap { #capability name, cmd... + typeset capname="$1" + capsh --drop=$capname -- -c "${*:2}" +} + +function has_cap { #capability name + typeset capname="$1" + typeset capeff=$(grep ^CapEff /proc/self/status) + capsh --decode="${capeff##*:}" | grep -q $capname +} + +function check_cap_support { + if ! command -v capsh > /dev/null ; then + log_unsupported "capsh program required to test device access" + fi + if ! has_cap cap_sys_admin ; then + log_unsupported "test user requires CAP_SYS_ADMIN capability" + fi + if ! has_cap cap_dac_override ; then + log_unsupported "test user requires CAP_DAC_OVERRIDE capability" + fi +} diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_add.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_add.ksh new file mode 100755 index 000000000000..5794a8440ca2 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_add.ksh @@ -0,0 +1,63 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/device_access/device_access.kshlib + +check_cap_support + +save_perms $DEV1 +save_perms $DEV2 + +function cleanup +{ + poolexists $TESTPOOL && destroy_pool $TESTPOOL + restore_perms $DEV1 + restore_perms $DEV2 +} +log_onexit cleanup + +log_assert 'device permissions are properly checked for zpool add' + +# make device accessible to everyone, ensure pool can be created and devices +# added. +log_must chmod 666 $DEV1 $DEV2 +log_must zpool create $TESTPOOL $DEV1 +log_must zpool add $TESTPOOL spare $DEV2 +check_vdevs $TESTPOOL $DEV1 $DEV2 +log_must zpool destroy $TESTPOOL + +# remove perms from devices, check pool can be created and device added. this +# is relying on the running user having CAP_DAC_OVERRIDE to bypass the +# permission check +log_must chmod 000 $DEV1 $DEV2 +log_must zpool create $TESTPOOL $DEV1 +log_must zpool add $TESTPOOL spare $DEV2 +check_vdevs $TESTPOOL $DEV1 $DEV2 +log_must zpool destroy $TESTPOOL + +# recreate the pool, then try to add it without CAP_DAC_OVERRIDE. this should +# fail, as the user has no direct permission and has no override capability. +log_must zpool create $TESTPOOL $DEV1 +log_mustnot without_cap cap_dac_override zpool add $TESTPOOL spare $DEV2 + +# confirm that the device was really not added, not just that the call failed +check_vdevs $TESTPOOL $DEV1 + +log_must zpool destroy $TESTPOOL + +log_pass 'device permissions are properly checked for zpool add' diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_attach.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_attach.ksh new file mode 100755 index 000000000000..2cd1d1df1e66 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_attach.ksh @@ -0,0 +1,63 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/device_access/device_access.kshlib + +check_cap_support + +save_perms $DEV1 +save_perms $DEV2 + +function cleanup +{ + poolexists $TESTPOOL && destroy_pool $TESTPOOL + restore_perms $DEV1 + restore_perms $DEV2 +} +log_onexit cleanup + +log_assert 'device permissions are properly checked for zpool attach' + +# make device accessible to everyone, ensure pool can be created and devices +# attached. +log_must chmod 666 $DEV1 $DEV2 +log_must zpool create $TESTPOOL $DEV1 +log_must zpool attach $TESTPOOL $DEV1 $DEV2 +check_vdevs $TESTPOOL $DEV1 $DEV2 +log_must zpool destroy $TESTPOOL + +# remove perms from devices, check pool can be created and device attached. +# this is relying on the running user having CAP_DAC_OVERRIDE to bypass the +# permission check +log_must chmod 000 $DEV1 $DEV2 +log_must zpool create $TESTPOOL $DEV1 +log_must zpool attach $TESTPOOL $DEV1 $DEV2 +check_vdevs $TESTPOOL $DEV1 $DEV2 +log_must zpool destroy $TESTPOOL + +# recreate the pool, then try to attach without CAP_DAC_OVERRIDE. this should +# fail, as the user has no direct permission and has no override capability. +log_must zpool create $TESTPOOL $DEV1 +log_mustnot without_cap cap_dac_override zpool attach $TESTPOOL $DEV1 $DEV2 + +# confirm that the device was really not attached, not just that the call failed +check_vdevs $TESTPOOL $DEV1 + +log_must zpool destroy $TESTPOOL + +log_pass 'device permissions are properly checked for zpool attach' diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_create.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_create.ksh new file mode 100755 index 000000000000..345af7e3645b --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_create.ksh @@ -0,0 +1,62 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/device_access/device_access.kshlib + +check_cap_support + +save_perms $DEV1 +save_perms $DEV2 + +function cleanup +{ + poolexists $TESTPOOL && destroy_pool $TESTPOOL + restore_perms $DEV1 + restore_perms $DEV2 +} +log_onexit cleanup + +log_assert 'device permissions are properly checked for zpool create' + +# make device accessible to everyone, ensure pool can be created +log_must chmod 666 $DEV1 +log_must zpool create $TESTPOOL $DEV1 +check_vdevs $TESTPOOL $DEV1 +log_must zpool destroy $TESTPOOL + +# remove perms from device, check pool can be created. this is relying +# on the running user having CAP_DAC_OVERRIDE to bypass the permission check +log_must chmod 000 $DEV1 +log_must zpool create $TESTPOOL $DEV1 +check_vdevs $TESTPOOL $DEV1 +log_must zpool destroy $TESTPOOL + +# now remove CAP_DAC_OVERRIDE and try again. this should fail, as the user +# has no direct permission and has no override capability. +log_mustnot without_cap cap_dac_override zpool create $TESTPOOL $DEV1 + +# confirm the pool was really not created, not just that the call failed +log_mustnot poolexists $TESTPOOL + +# create a pool with two devs, only one unavailable. ensure that the entire +# entire pool fails to be created, not just rejecting the inaccessible devices +log_must chmod 666 $DEV2 +log_mustnot without_cap cap_dac_override zpool create $TESTPOOL $DEV1 $DEV2 +log_mustnot poolexists $TESTPOOL + +log_pass 'device permissions are properly checked for zpool create' diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_import.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_import.ksh new file mode 100755 index 000000000000..a91e3f2e509e --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/device_access_import.ksh @@ -0,0 +1,69 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/device_access/device_access.kshlib + +check_cap_support + +save_perms $DEV1 +save_perms $DEV2 + +typeset tmpcache=$(mktemp) + +function cleanup +{ + poolexists $TESTPOOL && destroy_pool $TESTPOOL + restore_perms $DEV1 + restore_perms $DEV2 + rm -f $tmpcache +} +log_onexit cleanup + +log_assert 'device permissions are properly checked for zpool import' + +# create a pool with two devices, capture the cache file, and export it +log_must chmod 666 $DEV1 $DEV2 +log_must zpool create $TESTPOOL $DEV1 $DEV2 +check_vdevs $TESTPOOL $DEV1 $DEV2 +cp /etc/zfs/zpool.cache $tmpcache +log_must zpool export $TESTPOOL + +# remove perms from devices, check pool can be imported. this is relying on the +# running user having CAP_DAC_OVERRIDE to bypass the permission check +log_must chmod 000 $DEV1 $DEV2 +log_must zpool import $TESTPOOL +check_vdevs $TESTPOOL $DEV1 $DEV2 +log_must zpool export $TESTPOOL + +# try to import the pool without CAP_DAC_OVERRIDE. this should fail, as the +# user has no direct permission and has no override capability. +log_mustnot without_cap cap_dac_override zpool import $TESTPOOL +log_mustnot poolexists $TESTPOOL + +# make one device accessible, and try to import again. should fail, because +# the userspace device scan can't access all the devices +log_must chmod 666 $DEV1 +log_mustnot without_cap cap_dac_override zpool import $TESTPOOL +log_mustnot poolexists $TESTPOOL + +# try to import using the cachefile. should fail, because one of the devices +# listed in the cachefile is not accessible to the user +log_mustnot without_cap cap_dac_override zpool import -c $tmpcache $TESTPOOL +log_mustnot poolexists $TESTPOOL + +log_pass 'device permissions are properly checked for zpool import' diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/setup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/setup.ksh new file mode 100755 index 000000000000..0e174fc30487 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/device_access/setup.ksh @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/device_access/device_access.kshlib + +verify_runnable "global" + +log_pass diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/devices/devices_common.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/devices/devices_common.kshlib index 8024067ac9e4..3298b49fec7c 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/devices/devices_common.kshlib +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/devices/devices_common.kshlib @@ -54,9 +54,9 @@ function create_dev_file # %t - major device type in hex # %T - minor device type in hex # - major=$(stat --dereference --format="%t" "$devstr") - minor=$(stat --dereference --format="%T" "$devstr") - log_must mknod $filename b "0x${major}" "0x${minor}" + major=$(printf '%d' 0x$(stat -L -c "%t" "$devstr")) + minor=$(printf '%d' 0x$(stat -L -c "%T" "$devstr")) + log_must mknod $filename b "${major}" "${minor}" ;; *) # @@ -83,9 +83,9 @@ function create_dev_file # %t - major device type in hex # %T - minor device type in hex # - major=$(stat --format="%t" /dev/null) - minor=$(stat --format="%T" /dev/null) - log_must mknod $filename c "0x${major}" "0x${minor}" + major=$(printf '%d' 0x$(stat -c "%t" /dev/null)) + minor=$(printf '%d' 0x$(stat -c "%T" /dev/null)) + log_must mknod $filename c "${major}" "${minor}" ;; FreeBSD) # diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/direct/dio_write_verify.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/direct/dio_write_verify.ksh index 06b8fe2126db..6443bb4f19fe 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/direct/dio_write_verify.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/direct/dio_write_verify.ksh @@ -60,6 +60,8 @@ function cleanup # Clearing out dio_verify from event logs log_must zpool events -c log_must set_tunable32 VDEV_DIRECT_WR_VERIFY $DIO_WR_VERIFY_TUNABLE + log_must set_tunable32 DIO_WRITE_VERIFY_EVENTS_PER_SECOND \ + $DIO_WR_EVENTS_TUNABLE } log_assert "Verify checksum verify works for Direct I/O writes." @@ -75,6 +77,16 @@ NUMBLOCKS=300 BS=$((128 * 1024)) # 128k mntpnt=$(get_prop mountpoint $TESTPOOL/$TESTFS) typeset DIO_WR_VERIFY_TUNABLE=$(get_tunable VDEV_DIRECT_WR_VERIFY) +typeset DIO_WR_EVENTS_TUNABLE=$(get_tunable DIO_WRITE_VERIFY_EVENTS_PER_SECOND) + +# +# Each iteration below generates Direct I/O write verify failures and checks +# that dio_verify_wr zevents were posted. Those zevents are rate limited by +# zfs_dio_write_verify_events_per_second and the limiter state persists across +# iterations, so raise the limit while the test runs to avoid dropping the +# events being counted. The original value is restored in cleanup. +# +log_must set_tunable32 DIO_WRITE_VERIFY_EVENTS_PER_SECOND 1000000 # Get a list of vdevs in our pool set -A array $(get_disklist_fullpath $TESTPOOL) diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/events/events_common.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/events/events_common.kshlib index 79813e33bc77..89b25c8f654a 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/events/events_common.kshlib +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/events/events_common.kshlib @@ -40,7 +40,7 @@ function file_wait_event # file event timeout SECONDS=0 - until grep -q "^ZEVENT_CLASS=$event" $ZED_DEBUG_LOG ; do + until grep -q "^ZEVENT_CLASS=$event" "$file" ; do if [[ $SECONDS -gt $timeout ]]; then echo file_wait_event exceeded $SECONDS seconds return 1 diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/fallocate/fallocate_extend_timestamps.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/fallocate/fallocate_extend_timestamps.ksh new file mode 100755 index 000000000000..e96e3259d141 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/fallocate/fallocate_extend_timestamps.ksh @@ -0,0 +1,77 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2026 by iXsystems, Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/math.shlib + +# +# DESCRIPTION: +# A fallocate(2) that extends a file past EOF must update mtime/ctime; a +# FALLOC_FL_KEEP_SIZE preallocation must not change the size or timestamps. +# +# STRATEGY: +# 1. Create a small file and record its mtime/ctime +# 2. fallocate -l to a larger size (extend) and verify the size grew and both +# mtime and ctime advanced +# 3. fallocate --keep-size to a larger length and verify the size and both +# timestamps are unchanged +# + +verify_runnable "global" + +FILE=$TESTDIR/$TESTFILE0 +STARTSIZE=$((128 * 1024)) + +function cleanup +{ + [[ -f $FILE ]] && rm -f $FILE +} + +# extend past EOF: size must grow and mtime/ctime must advance +function verify_extend # <newsize> +{ + typeset -i size="$1" + + log_must mkfile $STARTSIZE $FILE + typeset -i tm="$(stat -c %Y $FILE)" + typeset -i tc="$(stat -c %Z $FILE)" + log_must sleep 1 + log_must fallocate -l $size $FILE + verify_eq $size "$(stat_size $FILE)" "size" + verify_ne $tm "$(stat -c %Y $FILE)" "mtime" + verify_ne $tc "$(stat -c %Z $FILE)" "ctime" + log_must rm -f $FILE +} + +log_assert "Ensure fallocate extend updates mtime/ctime and --keep-size does not" +log_onexit cleanup + +verify_extend $((256 * 1024)) +verify_extend $((1024 * 1024)) + +# --keep-size preallocation must not change size or timestamps +log_must mkfile $STARTSIZE $FILE +typeset -i tm="$(stat -c %Y $FILE)" +typeset -i tc="$(stat -c %Z $FILE)" +log_must sleep 1 +log_must fallocate --keep-size -l $((1024 * 1024)) $FILE +verify_eq $STARTSIZE "$(stat_size $FILE)" "size" +verify_eq $tm "$(stat -c %Y $FILE)" "mtime" +verify_eq $tc "$(stat -c %Z $FILE)" "ctime" +log_must rm -f $FILE + +log_pass "fallocate extend correctly updates timestamps" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/migration/setup.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/migration/setup.ksh index 0d0c080c1524..4fc5a9a5022d 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/migration/setup.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/migration/setup.ksh @@ -58,9 +58,29 @@ log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS rm -rf $NONZFS_TESTDIR || log_unresolved Could not remove $NONZFS_TESTDIR mkdir -p $NONZFS_TESTDIR || log_unresolved Could not create $NONZFS_TESTDIR +# +# $NONZFS_DISK may still carry a zfs_member label from a pool used by an +# earlier test. 'zpool destroy' leaves the vdev labels in place and new_fs +# only overwrites the front of the device, so the trailing labels can +# survive. libblkid then probes the device as ambiguous (both the new +# filesystem and zfs_member) and the auto-detecting mount below fails +# intermittently. Wipe any residual signatures first so the new filesystem +# is unambiguous. Skip the single-disk case, where $NONZFS_DISK is the same +# device the test pool was just created on. +# +if is_linux && [[ "$NONZFS_DISK" != "$ZFS_DISK" ]]; then + log_must wipefs -a ${DEV_DSKDIR}/$NONZFS_DISK +fi + new_fs ${DEV_DSKDIR}/$NONZFS_DISK || log_untested "Unable to setup a $NEWFS_DEFAULT_FS file system" +# +# Let udev settle so the device node reflects the new filesystem before the +# auto-detecting mount consults it. +# +block_device_wait ${DEV_DSKDIR}/$NONZFS_DISK + log_must mount ${DEV_DSKDIR}/$NONZFS_DISK $NONZFS_TESTDIR log_pass diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp.kshlib index 8717b1912fef..841a5e9d2694 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp.kshlib +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp.kshlib @@ -83,10 +83,11 @@ function mmp_clear_hostid rm -f $HOSTID_FILE } -function mmp_pool_create_simple # pool dir +function mmp_pool_create_simple # pool dir enriched { typeset pool=${1:-$MMP_POOL} typeset dir=${2:-$MMP_DIR} + typeset enriched=${3:-""} log_must mkdir -p $dir log_must rm -f $dir/* @@ -96,6 +97,28 @@ function mmp_pool_create_simple # pool dir log_must mmp_set_hostid $HOSTID1 log_must zpool create -f -o cachefile=$MMP_CACHE $pool \ mirror $dir/vdev1 $dir/vdev2 + + if [[ -n "$enriched" ]]; then + # + # Add then remove a data vdev to leave an indirect vdev, and + # add then remove a log to leave a hole vdev, so the pool + # exercises the non-writeable top-level vdev types skipped by + # the MMP claim check. Round it out with a log, a cache, and + # a spare vdev. + # + log_must truncate -s $MINVDEVSIZE $dir/vdev3 $dir/vdev4 \ + $dir/vdev5 $dir/vdev6 $dir/vdev7 + log_must zpool add -f $pool $dir/vdev3 + log_must zpool remove $pool $dir/vdev3 + log_must zpool wait -t remove $pool + log_must sync_pool $pool + log_must zpool add -f $pool log $dir/vdev4 + log_must zpool remove $pool $dir/vdev4 + log_must zpool add -f $pool log $dir/vdev5 + log_must zpool add -f $pool cache $dir/vdev6 + log_must zpool add -f $pool spare $dir/vdev7 + fi + log_must zpool set multihost=on $pool } @@ -116,7 +139,7 @@ function mmp_pool_create_zhack # pool dir log_must eval "ZFS_HOSTID=$HOSTID1 zhack $opts >$MMP_ZHACK_LOG 2>&1 &" while ! is_pool_imported "$pool" "-d $dir"; do - log_must pgrep zhack + log_must pgrep -x zhack log_must sleep 5 done } @@ -126,7 +149,14 @@ function mmp_pool_destroy # pool dir typeset pool=${1:-$MMP_POOL} typeset dir=${2:-$MMP_DIR} - ZHACKPID=$(pgrep zhack) + # + # -x matches the process name exactly. Without it any process whose + # name merely contains "zhack" is matched, which includes a test script + # named after the tool: a ksh script mmp_zhack_reclaim.ksh has comm + # "mmp_zhack_recla", so the bare form found the running test and killed + # it, leaving the harness with a dead test and nothing logged. + # + ZHACKPID=$(pgrep -x zhack) if [ -n "$ZHACKPID" ]; then log_must kill $ZHACKPID wait $ZHACKPID @@ -148,7 +178,7 @@ function mmp_pool_set_hostid # pool hostid log_must mmp_clear_hostid log_must mmp_set_hostid $hostid log_must zpool export $pool - log_must zpool import $pool + log_must zpool import -d $MMP_DIR $pool return 0 } diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_concurrent_import.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_concurrent_import.ksh index 7fab2634ded1..99b099f9b870 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_concurrent_import.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_concurrent_import.ksh @@ -93,7 +93,7 @@ log_assert "multihost=on concurrent imports" log_onexit cleanup # 1. Create a multihost enabled pool with HOSTID1 -mmp_pool_create_simple $MMP_POOL $MMP_DIR +mmp_pool_create_simple $MMP_POOL $MMP_DIR enriched log_must zpool export -F $MMP_POOL # 2. zhack imports: $HOSTID1 (matching) and $HOSTID1 (matching) diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_degraded_import.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_degraded_import.ksh new file mode 100755 index 000000000000..4f1c0d1b010c --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_degraded_import.ksh @@ -0,0 +1,159 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# DESCRIPTION: +# Verify the MMP uberblock claim requires a write only to those mirror +# legs the pool configuration still expects to be present. +# +# STRATEGY: +# 1. Create a mirrored pool with multihost=on and export it without +# updating the labels, so it still appears imported and the next +# import runs an activity check and an uberblock claim. +# 2. A healthy mirror is claimed and imported. +# 3. A mirror with an offlined leg is claimed and imported degraded. +# The offline state is recorded in the configuration, so that leg +# is not required and a degraded pool can still fail over. +# 4. A mirror with a leg this host cannot open, and which the +# configuration does not mark absent, is refused. Requiring that +# leg is what prevents two hosts which each see one leg from +# importing the same pool. +# 5. The same two cases on a three-way mirror, where the number of legs +# the configuration expects and the number this host can reach come +# apart. A claim which stopped at two writes would accept the last +# case, since two of the three legs are readable. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/mmp/mmp.cfg +. $STF_SUITE/tests/functional/mmp/mmp.kshlib + +verify_runnable "both" + +# +# A leg moved out of the search directory entirely. Renaming it in place +# would not do: the devices are matched by the labels they carry rather than by +# name, so a renamed leg is still found and opened. +# +typeset HIDDEN_LEG=$TEST_BASE_DIR/mmp_degraded_hidden_leg + +function cleanup +{ + mmp_pool_destroy $MMP_POOL $MMP_DIR + log_must rm -f $HIDDEN_LEG + log_must mmp_clear_hostid +} + +log_assert "mmp claim requires only the mirror legs the config expects" +log_onexit cleanup + +# +# Leave the pool looking imported by exporting it without updating the +# labels, then take on a different identity. The next import therefore +# runs an activity check and an uberblock claim, as it would after the +# original host failed. +# +function crash_export_as_other_host +{ + log_must zpool export -F $MMP_POOL + log_must mmp_clear_hostid + log_must mmp_set_hostid $HOSTID2 +} + +# 1. A healthy mirror is claimed and imported +log_note "Verify a healthy mirror is claimed and imported" +mmp_pool_create_simple $MMP_POOL $MMP_DIR +crash_export_as_other_host +log_must zpool import -d $MMP_DIR -f $MMP_POOL +log_must check_state $MMP_POOL "" "ONLINE" +mmp_pool_destroy $MMP_POOL $MMP_DIR + +# 2. A mirror with an offlined leg is claimed and imported degraded +log_note "Verify a mirror with an offlined leg is claimed and imported" +mmp_pool_create_simple $MMP_POOL $MMP_DIR +log_must zpool offline $MMP_POOL $MMP_DIR/vdev2 +log_must check_state $MMP_POOL $MMP_DIR/vdev2 "OFFLINE" +crash_export_as_other_host +log_must zpool import -d $MMP_DIR -f $MMP_POOL +log_must check_state $MMP_POOL "" "DEGRADED" +log_must check_state $MMP_POOL $MMP_DIR/vdev2 "OFFLINE" +mmp_pool_destroy $MMP_POOL $MMP_DIR + +# 3. A mirror with a leg which is unreadable, and not marked absent by +# the configuration, is refused +log_note "Verify a mirror with an unreachable leg is refused" +mmp_pool_create_simple $MMP_POOL $MMP_DIR +crash_export_as_other_host +log_must mv $MMP_DIR/vdev2 $HIDDEN_LEG + +typeset out +if out=$(zpool import -d $MMP_DIR -f $MMP_POOL 2>&1); then + log_fail "Imported a mirror while a required leg was unreadable" +fi +if ! echo "$out" | grep -q "could not be written to a device"; then + log_fail "Import refused for an unexpected reason: $out" +fi + +# +# The cases above use a two-way mirror, where "every present leg" and "two +# legs" are the same number. A three-way mirror separates them. +# +function mmp_pool_create_3way +{ + log_must mkdir -p $MMP_DIR + log_must rm -f $MMP_DIR/* + log_must truncate -s $MINVDEVSIZE $MMP_DIR/vdev1 $MMP_DIR/vdev2 \ + $MMP_DIR/vdev3 + log_must mmp_clear_hostid + log_must mmp_set_hostid $HOSTID1 + log_must zpool create -f -o cachefile=$MMP_CACHE $MMP_POOL \ + mirror $MMP_DIR/vdev1 $MMP_DIR/vdev2 $MMP_DIR/vdev3 + log_must zpool set multihost=on $MMP_POOL +} + +log_must rm -f $HIDDEN_LEG + +# 4. A three-way mirror with an offlined leg is claimed and imported degraded +log_note "Verify a three-way mirror with an offlined leg is claimed" +mmp_pool_create_3way +log_must zpool offline $MMP_POOL $MMP_DIR/vdev3 +log_must check_state $MMP_POOL $MMP_DIR/vdev3 "OFFLINE" +crash_export_as_other_host +log_must zpool import -d $MMP_DIR -f $MMP_POOL +log_must check_state $MMP_POOL "" "DEGRADED" +log_must check_state $MMP_POOL $MMP_DIR/vdev3 "OFFLINE" +mmp_pool_destroy $MMP_POOL $MMP_DIR + +# +# 5. A three-way mirror with one leg this host cannot open, and which the +# configuration does not mark absent, is refused. Two of the three legs +# are readable here, so a claim which required only two writes would let +# this import while another host holding the third leg could do the same. +# +log_note "Verify a three-way mirror with an unreachable leg is refused" +mmp_pool_create_3way +crash_export_as_other_host +log_must mv $MMP_DIR/vdev3 $HIDDEN_LEG + +if out=$(zpool import -d $MMP_DIR -f $MMP_POOL 2>&1); then + log_fail "Imported a three-way mirror while a required leg was" \ + "unreadable" +fi +if ! echo "$out" | grep -q "could not be written to a device"; then + log_fail "Import refused for an unexpected reason: $out" +fi + +log_pass "mmp claim requires only the mirror legs the config expects" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh index d7846ea90a6a..bb3a4bfe79b8 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh @@ -40,34 +40,34 @@ verify_runnable "both" function cleanup { - datasetexists $TESTPOOL && destroy_pool $TESTPOOL + mmp_pool_destroy $MMP_POOL $MMP_DIR log_must mmp_clear_hostid } log_assert "multihost=on|off activity checks exported pool" log_onexit cleanup -# 1. Create a zpool -log_must mmp_set_hostid $HOSTID1 -log_must zpool create -f $TESTPOOL $DISK +# 1. Create an enriched zpool (hole, indirect, log, cache, and spare +# vdevs) so the activity checks exercise the non-writeable types. +mmp_pool_create_simple $MMP_POOL $MMP_DIR enriched # 2. Verify multihost=off and hostids match (no activity check) log_note "Verify multihost=off and hostids match (no activity check)" -log_must zpool set multihost=off $TESTPOOL +log_must zpool set multihost=off $MMP_POOL for opt in "" "-f"; do - log_must zpool export $TESTPOOL - log_must import_no_activity_check $TESTPOOL $opt + log_must zpool export $MMP_POOL + log_must import_no_activity_check $MMP_POOL "-d $MMP_DIR $opt" done # 3. Verify multihost=off and hostids differ (no activity check) log_note "Verify multihost=off and hostids differ (no activity check)" for opt in "" "-f"; do - log_must mmp_pool_set_hostid $TESTPOOL $HOSTID1 - log_must zpool export $TESTPOOL + log_must mmp_pool_set_hostid $MMP_POOL $HOSTID1 + log_must zpool export $MMP_POOL log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 - log_must import_no_activity_check $TESTPOOL $opt + log_must import_no_activity_check $MMP_POOL "-d $MMP_DIR $opt" done # 4. Verify multihost=off and hostid zero allowed (no activity check) @@ -75,39 +75,39 @@ log_note "Verify multihost=off and hostid zero allowed (no activity check)" log_must mmp_clear_hostid for opt in "" "-f"; do - log_must zpool export $TESTPOOL - log_must import_no_activity_check $TESTPOOL $opt + log_must zpool export $MMP_POOL + log_must import_no_activity_check $MMP_POOL "-d $MMP_DIR $opt" done # 5. Verify multihost=on and hostids match (no activity check) log_note "Verify multihost=on and hostids match (no activity check)" -log_must mmp_pool_set_hostid $TESTPOOL $HOSTID1 -log_must zpool set multihost=on $TESTPOOL +log_must mmp_pool_set_hostid $MMP_POOL $HOSTID1 +log_must zpool set multihost=on $MMP_POOL for opt in "" "-f"; do - log_must zpool export $TESTPOOL - log_must import_no_activity_check $TESTPOOL $opt + log_must zpool export $MMP_POOL + log_must import_no_activity_check $MMP_POOL "-d $MMP_DIR $opt" done # 6. Verify multihost=on and hostids differ (activity check) log_note "Verify multihost=on and hostids differ (activity check)" for opt in "" "-f"; do - log_must mmp_pool_set_hostid $TESTPOOL $HOSTID1 - log_must zpool export $TESTPOOL + log_must mmp_pool_set_hostid $MMP_POOL $HOSTID1 + log_must zpool export $MMP_POOL log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 - log_must import_activity_check $TESTPOOL $opt + log_must import_activity_check $MMP_POOL "-d $MMP_DIR $opt" done # 7. Verify multihost=on and hostid zero fails (no activity check) log_note "Verify multihost=on and hostid zero fails (no activity check)" -log_must zpool export $TESTPOOL +log_must zpool export $MMP_POOL log_must mmp_clear_hostid for opt in "" "-f"; do MMP_IMPORTED_MSG="Set a unique system hostid" - log_must check_pool_import $TESTPOOL "" "action" "$MMP_IMPORTED_MSG" - log_mustnot import_no_activity_check $TESTPOOL $opt + log_must check_pool_import $MMP_POOL "-d $MMP_DIR" "action" "$MMP_IMPORTED_MSG" + log_mustnot import_no_activity_check $MMP_POOL "-d $MMP_DIR $opt" done log_pass "multihost=on|off exported pool activity checks passed" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh index 4c6b9c2e065f..3c09fcc0dfec 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh @@ -42,7 +42,7 @@ verify_runnable "both" function cleanup { - datasetexists $TESTPOOL && destroy_pool $TESTPOOL + mmp_pool_destroy $MMP_POOL $MMP_DIR log_must mmp_clear_hostid log_must set_tunable64 MULTIHOST_INTERVAL $MMP_INTERVAL_DEFAULT } @@ -50,63 +50,63 @@ function cleanup log_assert "multihost=on|off inactive pool activity checks" log_onexit cleanup -# 1. Create a zpool -log_must mmp_set_hostid $HOSTID1 -log_must zpool create -f $TESTPOOL $DISK +# 1. Create an enriched zpool (hole, indirect, log, cache, and spare +# vdevs) so the activity checks exercise the non-writeable types. +mmp_pool_create_simple $MMP_POOL $MMP_DIR enriched # 2. Verify multihost=off and hostids match (no activity check) log_note "Verify multihost=off and hostids match (no activity check)" -log_must zpool set multihost=off $TESTPOOL +log_must zpool set multihost=off $MMP_POOL for opt in "" "-f"; do - log_must zpool export -F $TESTPOOL - log_must import_no_activity_check $TESTPOOL $opt + log_must zpool export -F $MMP_POOL + log_must import_no_activity_check $MMP_POOL "-d $MMP_DIR $opt" done # 3. Verify multihost=off and hostids differ (no activity check) log_note "Verify multihost=off and hostids differ (no activity check)" -log_must zpool export -F $TESTPOOL +log_must zpool export -F $MMP_POOL log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 -log_mustnot import_no_activity_check $TESTPOOL "" -log_must import_no_activity_check $TESTPOOL "-f" +log_mustnot import_no_activity_check $MMP_POOL "-d $MMP_DIR" +log_must import_no_activity_check $MMP_POOL "-d $MMP_DIR -f" # 4. Verify multihost=off and hostid zero allowed (no activity check) log_note "Verify multihost=off and hostid zero allowed (no activity check)" -log_must zpool export -F $TESTPOOL +log_must zpool export -F $MMP_POOL log_must mmp_clear_hostid -log_mustnot import_no_activity_check $TESTPOOL "" -log_must import_no_activity_check $TESTPOOL "-f" +log_mustnot import_no_activity_check $MMP_POOL "-d $MMP_DIR" +log_must import_no_activity_check $MMP_POOL "-d $MMP_DIR -f" # 5. Verify multihost=on and hostids match (activity check) log_note "Verify multihost=on and hostids match (activity check)" -log_must mmp_pool_set_hostid $TESTPOOL $HOSTID1 -log_must zpool set multihost=on $TESTPOOL +log_must mmp_pool_set_hostid $MMP_POOL $HOSTID1 +log_must zpool set multihost=on $MMP_POOL for opt in "" "-f"; do - log_must zpool export -F $TESTPOOL - log_must import_activity_check $TESTPOOL $opt + log_must zpool export -F $MMP_POOL + log_must import_activity_check $MMP_POOL "-d $MMP_DIR $opt" done # 6. Verify multihost=on and hostids differ (activity check) log_note "Verify multihost=on and hostids differ (activity check)" -log_must zpool export -F $TESTPOOL +log_must zpool export -F $MMP_POOL log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 -log_mustnot import_activity_check $TESTPOOL "" -log_must import_activity_check $TESTPOOL "-f" +log_mustnot import_activity_check $MMP_POOL "-d $MMP_DIR" +log_must import_activity_check $MMP_POOL "-d $MMP_DIR -f" # 7. Verify mmp_write and mmp_fail are set correctly log_note "Verify mmp_write and mmp_fail are set correctly" -log_must zpool export -F $TESTPOOL -log_must verify_mmp_write_fail_present ${DISK[0]} +log_must zpool export -F $MMP_POOL +log_must verify_mmp_write_fail_present $MMP_DIR/vdev1 # 8. Verify multihost=on and hostid zero fails (no activity check) log_note "Verify multihost=on and hostid zero fails (no activity check)" log_must mmp_clear_hostid MMP_IMPORTED_MSG="Set a unique system hostid" -log_must check_pool_import $TESTPOOL "-f" "action" "$MMP_IMPORTED_MSG" -log_mustnot import_no_activity_check $TESTPOOL "-f" +log_must check_pool_import $MMP_POOL "-d $MMP_DIR -f" "action" "$MMP_IMPORTED_MSG" +log_mustnot import_no_activity_check $MMP_POOL "-d $MMP_DIR -f" # 9. Verify activity check duration based on mmp_write and mmp_fail # Specify a short test via tunables but import pool imported while @@ -115,6 +115,6 @@ log_note "Verify activity check duration based on mmp_write and mmp_fail" log_must set_tunable64 MULTIHOST_INTERVAL $MMP_INTERVAL_MIN log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID1 -log_must import_activity_check $TESTPOOL "-f" +log_must import_activity_check $MMP_POOL "-d $MMP_DIR -f" log_pass "multihost=on|off inactive pool activity checks passed" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_zhack_reclaim.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_zhack_reclaim.ksh new file mode 100755 index 000000000000..507c9648284d --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/mmp/mmp_zhack_reclaim.ksh @@ -0,0 +1,305 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/mmp/mmp.cfg +. $STF_SUITE/tests/functional/mmp/mmp.kshlib + +# +# DESCRIPTION: +# Verify `zhack mmp reclaim` recovers a pool whose configuration expects +# mirror legs that went away with a failed host, and that it relaxes the +# uberblock claim no further than that. +# +# STRATEGY: +# 1. A pool stranded by legs the configuration still expects is +# recovered, and the claim then accepts it on an ordinary import. +# The import is made as a THIRD hostid on purpose: zhack exports +# cleanly under its own hostid, so importing again as the same host +# would skip the activity check and never exercise the claim at all. +# 2. The claim is relaxed only for legs this host cannot open. A pool +# held by a live host which shares a leg with us is still refused. +# 3. Legs of every top level vdev are still counted after a recovery. +# 4. A pool which does not use multihost is left alone, since no claim +# runs for it and an ordinary import already succeeds. +# 5. Legs of a log vdev are not touched, since the claim never counts +# them. +# 6. Members of a raidz vdev are not touched. The claim requires +# parity+1 writes in aggregate rather than one per member, so an +# absent member does not raise the requirement and there is nothing +# for this tool to recover. +# + +verify_runnable "both" + +# $HOSTID3 comes from mmp.cfg. A third identity is needed so a post-recovery +# import cannot take the exported-and-same-hostid shortcut past the activity +# check, which would leave the claim unexercised. +typeset HIDDEN_LEG=$TEST_BASE_DIR/mmp_zhack_hidden_leg +typeset PEER_DIR=$TEST_BASE_DIR/mmp_zhack_peer +typeset DBGMSG=/proc/spl/kstat/zfs/dbgmsg +typeset MMP_IMPORT_ERR=$TEST_BASE_DIR/mmp_zhack_import_err + +function cleanup +{ + mmp_pool_destroy $MMP_POOL $MMP_DIR + rm -rf $HIDDEN_LEG $PEER_DIR $MMP_IMPORT_ERR + log_must mmp_clear_hostid +} + +log_assert "zhack mmp reclaim recovers a stranded pool and relaxes no further" +log_onexit cleanup + +# +# Leave the pool looking imported, then take on a different identity, so the +# next import runs an activity check and an uberblock claim as it would after +# the original host failed. +# +function crash_export_as_other_host +{ + log_must zpool export -F $MMP_POOL + log_must mmp_clear_hostid + log_must mmp_set_hostid $HOSTID2 +} + +# +# Become a third host. Kept out of claim_reimport because log_must writes +# its SUCCESS lines to stdout, which would be captured along with the +# numbers by the command substitution below. +# +function become_third_host +{ + log_must mmp_clear_hostid + log_must mmp_set_hostid $HOSTID3 +} + +# +# Import and echo the claim's own accounting, "req good". Empty if the +# import failed. Nothing here may write to stdout except the numbers. +# +function claim_reimport +{ + typeset re='.*req_writes=([0-9]+) issued_writes=[0-9]+' + re="$re good_writes=([0-9]+).*" + + echo 0 >$DBGMSG + + zpool import -d $MMP_DIR $MMP_POOL >/dev/null 2>&1 || return + + grep -h 'mmp: claiming uberblock' $DBGMSG 2>/dev/null | + sed -nE "s/$re/\1 \2/p" | + tail -1 +} + +# 1. A stranded pool is recovered, and the claim accepts it afterwards +log_note "Verify a stranded pool is recovered and the claim then accepts it" +mmp_pool_create_simple $MMP_POOL $MMP_DIR +crash_export_as_other_host +log_must mv $MMP_DIR/vdev2 $HIDDEN_LEG + +# +# The import has to say which of the two refusals this is. It used to report +# the same "pool is imported on host" text used for a pool another host holds, +# which is wrong here: nothing holds this pool, the claim simply could not +# reach every leg the config expects. Both directions matter, so the wrong +# message is asserted against as well as the right one. +# +log_mustnot eval "zpool import -d $MMP_DIR -f $MMP_POOL >$MMP_IMPORT_ERR 2>&1" +log_must grep -q "could not be written to a device" $MMP_IMPORT_ERR +log_mustnot grep -q "pool is imported on host" $MMP_IMPORT_ERR +log_must rm -f $MMP_IMPORT_ERR + +log_must zhack -d $MMP_DIR mmp reclaim $MMP_POOL + +# +# zhack exports the pool when it is done, so there is nothing to inspect +# until the pool is imported again. That import is the point of the test. +# +become_third_host +typeset nums=$(claim_reimport) +if [[ "$nums" != "1 1" ]]; then + log_fail "claim after recovery counted '$nums', expected '1 1'" +fi +log_note "claim after recovery required 1 write and got 1" +log_must check_state $MMP_POOL "" "DEGRADED" + +# +# The leg has to be left OFFLINE specifically. A faulted leg would also read +# as DEGRADED and would also satisfy the claim, but only an offline one has +# "zpool online" as its documented inverse. +# +if ! zpool status $MMP_POOL | grep -q OFFLINE; then + log_fail "the absent leg was not left offline after recovery" +fi +mmp_pool_destroy $MMP_POOL $MMP_DIR +log_must rm -f $HIDDEN_LEG + +# 2. A pool held by a live host which shares a leg with us is still refused +log_note "Verify a live host sharing a leg is still refused" +mmp_pool_create_simple $MMP_POOL $MMP_DIR +log_must zpool export $MMP_POOL + +# +# The peer keeps both legs. We keep a link to one of them, so we cannot open +# the other and the relaxed claim has something to forgive, while the peer's +# multihost writes still reach a leg we can read. +# +log_must mkdir -p $PEER_DIR +log_must mv $MMP_DIR/vdev1 $PEER_DIR/vdev1 +log_must mv $MMP_DIR/vdev2 $PEER_DIR/vdev2 +log_must ln $PEER_DIR/vdev1 $MMP_DIR/vdev1 + +log_must mmp_clear_hostid +log_must mmp_set_hostid $HOSTID2 +log_must eval "ZFS_HOSTID=$HOSTID1 zhack -d $PEER_DIR action idle -t120 \ + $MMP_POOL >$MMP_ZHACK_LOG 2>&1 &" + +# +# Wait for the peer to hold the pool the same way mmp_pool_create_zhack does. +# Grepping its log for the activity check line would work only by accident: +# ZFS_HOSTID is parsed with strtoull(env, NULL, 0), so a leading zero makes it +# octal, and the peer's hostid happens not to match the label. Write the +# hostid unambiguously and the check is skipped and the line never appears. +# +typeset -i tries=0 +while ! is_pool_imported $MMP_POOL "-d $PEER_DIR"; do + if (( tries++ > 30 )); then + log_fail "peer never imported the pool" + fi + log_must pgrep -x zhack + log_must sleep 4 +done + +log_mustnot zhack -d $MMP_DIR mmp reclaim $MMP_POOL +log_must pgrep -x zhack +log_must rm -f $MMP_ZHACK_LOG +mmp_pool_destroy $MMP_POOL $MMP_DIR +log_must rm -rf $PEER_DIR + +# 3. Every top level vdev is still counted after a recovery +log_note "Verify both top level vdevs are counted after a recovery" +log_must mkdir -p $MMP_DIR +log_must rm -f $MMP_DIR/* +log_must truncate -s $MINVDEVSIZE $MMP_DIR/vdev1 $MMP_DIR/vdev2 \ + $MMP_DIR/vdev3 $MMP_DIR/vdev4 +log_must mmp_clear_hostid +log_must mmp_set_hostid $HOSTID1 +log_must zpool create -f -o cachefile=$MMP_CACHE $MMP_POOL \ + mirror $MMP_DIR/vdev1 $MMP_DIR/vdev2 mirror $MMP_DIR/vdev3 $MMP_DIR/vdev4 +log_must zpool set multihost=on $MMP_POOL +crash_export_as_other_host +log_must mv $MMP_DIR/vdev4 $HIDDEN_LEG + +log_mustnot zpool import -d $MMP_DIR -f $MMP_POOL +log_must zhack -d $MMP_DIR mmp reclaim $MMP_POOL + +become_third_host +nums=$(claim_reimport) +if [[ "$nums" != "3 3" ]]; then + log_fail "claim across two top levels counted '$nums', expected '3 3'" +fi +log_note "claim across two top levels required 3 writes and got 3" +mmp_pool_destroy $MMP_POOL $MMP_DIR +log_must rm -f $HIDDEN_LEG + +# 4. A pool which does not use multihost is left alone +log_note "Verify a pool without multihost is left alone" +log_must mkdir -p $MMP_DIR +log_must rm -f $MMP_DIR/* +log_must truncate -s $MINVDEVSIZE $MMP_DIR/vdev1 $MMP_DIR/vdev2 +log_must mmp_clear_hostid +log_must mmp_set_hostid $HOSTID1 +log_must zpool create -f -o cachefile=$MMP_CACHE $MMP_POOL \ + mirror $MMP_DIR/vdev1 $MMP_DIR/vdev2 +log_must zpool export -F $MMP_POOL +log_must mmp_clear_hostid +log_must mmp_set_hostid $HOSTID2 +log_must mv $MMP_DIR/vdev2 $HIDDEN_LEG + +log_mustnot zhack -d $MMP_DIR mmp reclaim $MMP_POOL +log_must zpool import -d $MMP_DIR -f $MMP_POOL +log_must check_state $MMP_POOL "" "DEGRADED" +if zpool status $MMP_POOL | grep -q OFFLINE; then + log_fail "a leg was offlined on a pool which does not use multihost" +fi +mmp_pool_destroy $MMP_POOL $MMP_DIR +log_must rm -f $HIDDEN_LEG + +# 5. Legs of a log vdev are not touched +log_note "Verify a log vdev leg is left alone" +log_must mkdir -p $MMP_DIR +log_must rm -f $MMP_DIR/* +log_must truncate -s $MINVDEVSIZE $MMP_DIR/vdev1 $MMP_DIR/vdev2 \ + $MMP_DIR/vdev3 $MMP_DIR/vdev4 +log_must mmp_clear_hostid +log_must mmp_set_hostid $HOSTID1 +log_must zpool create -f -o cachefile=$MMP_CACHE $MMP_POOL \ + mirror $MMP_DIR/vdev1 $MMP_DIR/vdev2 \ + log mirror $MMP_DIR/vdev3 $MMP_DIR/vdev4 +log_must zpool set multihost=on $MMP_POOL +crash_export_as_other_host +log_must mv $MMP_DIR/vdev4 $HIDDEN_LEG + +log_must zhack -d $MMP_DIR mmp reclaim $MMP_POOL + +become_third_host +nums=$(claim_reimport) +if [[ "$nums" != "2 2" ]]; then + log_fail "pool with an absent log leg counted '$nums', expected '2 2'" +fi +log_note "pool with an absent log leg still imports, claim required 2 got 2" +if zpool status $MMP_POOL | grep -q OFFLINE; then + log_fail "a log vdev leg was offlined" +fi +mmp_pool_destroy $MMP_POOL $MMP_DIR +log_must rm -f $HIDDEN_LEG + +# 6. Members of a raidz vdev are not touched +log_note "Verify a raidz member is left alone" +log_must mkdir -p $MMP_DIR +log_must rm -f $MMP_DIR/* +log_must truncate -s $MINVDEVSIZE $MMP_DIR/vdev1 $MMP_DIR/vdev2 \ + $MMP_DIR/vdev3 $MMP_DIR/vdev4 +log_must mmp_clear_hostid +log_must mmp_set_hostid $HOSTID1 +log_must zpool create -f -o cachefile=$MMP_CACHE $MMP_POOL \ + raidz2 $MMP_DIR/vdev1 $MMP_DIR/vdev2 $MMP_DIR/vdev3 $MMP_DIR/vdev4 +log_must zpool set multihost=on $MMP_POOL +crash_export_as_other_host +log_must mv $MMP_DIR/vdev4 $HIDDEN_LEG + +# +# A raidz vdev is required as parity+1 in aggregate rather than one write per +# member, so an absent member does not raise the requirement. The claim holds +# while parity+1 members remain writeable, and offlining one would be a +# persistent change that buys nothing. +# +log_must eval "zhack -d $MMP_DIR mmp reclaim $MMP_POOL >$MMP_ZHACK_LOG 2>&1" +log_must grep -q "no absent leaves" $MMP_ZHACK_LOG +log_must rm -f $MMP_ZHACK_LOG + +become_third_host +nums=$(claim_reimport) +if [[ "$nums" != "3 3" ]]; then + log_fail "raidz2 with an absent member counted '$nums', expected '3 3'" +fi +log_note "raidz2 with an absent member imports, claim required 3 got 3" +if zpool status $MMP_POOL | grep -q OFFLINE; then + log_fail "a raidz member was offlined" +fi + +log_pass "zhack mmp reclaim recovers a stranded pool and relaxes no further" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/projectquota/projectspace_006_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/projectquota/projectspace_006_pos.ksh new file mode 100755 index 000000000000..29309c68e1bc --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/projectquota/projectspace_006_pos.ksh @@ -0,0 +1,97 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026, Gluesys. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib + +# +# DESCRIPTION: +# Verify 'zfs projectspace' reports correct quota values whether the +# dataset is mounted or not, and does not fail with EBUSY when +# raced against 'zfs mount'. +# +# STRATEGY: +# 1. Create a dataset with a projectquota and write 50M of data. +# 2. Check quota and used values while mounted. +# 3. Unmount and verify the same values are still readable. +# 4. Remount and re-verify values are consistent. +# 5. Race 'zfs projectspace' against 'zfs mount' 50 times; expect no EBUSY. +# + +function cleanup +{ + zfs unmount $RACE_FS 2>/dev/null + datasetexists $RACE_FS && destroy_dataset $RACE_FS + rm -f "$ERRFILE" "$DATAFILE" +} + +log_onexit cleanup + +typeset RACE_FS=$QFS/projectspace_race +typeset ERRFILE=/tmp/projectspace_006_err.$$ +typeset DATAFILE +typeset -i ITERS=50 + +log_assert "zfs projectspace shows correct quota values and does not EBUSY on mount race" + +log_must zfs create $RACE_FS +log_must zfs set projectquota@$PRJID1=100m $RACE_FS +mkmount_writable $RACE_FS +DATAFILE=$(get_prop mountpoint $RACE_FS)/projectspace_006_data.$$ +log_must mkfile 50m $DATAFILE +sync_all_pools + +log_note "check projectspace values while mounted" +log_must eval "zfs projectspace $RACE_FS | grep $PRJID1 | grep 100M" + +log_must zfs unmount $RACE_FS +log_note "check projectspace values while unmounted" +log_must eval "zfs projectspace $RACE_FS | grep $PRJID1 | grep 100M" + +log_must zfs mount $RACE_FS +log_note "check projectspace values after remount" +log_must eval "zfs projectspace $RACE_FS | grep $PRJID1 | grep 100M" + +typeset -i i=0 ebusy=0 +while (( i < ITERS )); do + log_must zfs unmount $RACE_FS + zfs mount $RACE_FS & + typeset mpid=$! + zfs projectspace $RACE_FS >"$ERRFILE" 2>&1 + typeset rc=$? + wait $mpid + if (( rc != 0 )) && grep -qi "busy" "$ERRFILE" 2>/dev/null; then + (( ebusy++ )) + log_note "Iteration $i: EBUSY: $(cat $ERRFILE)" + fi + rm -f "$ERRFILE" + (( i++ )) +done + +(( ebusy > 0 )) && log_fail "EBUSY seen $ebusy/$ITERS times" + +log_pass "zfs projectspace shows correct quota values and does not EBUSY on mount race" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend.kshlib b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend.kshlib index b696d60741c0..9fd2ecb959db 100644 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend.kshlib +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend.kshlib @@ -583,14 +583,34 @@ function file_check { sendfs=$1 recvfs=$2 + typeset -i compared=0 - if [[ -d /$recvfs/.zfs/snapshot/a && -d \ - /$sendfs/.zfs/snapshot/a ]]; then - log_must directory_diff /$recvfs/.zfs/snapshot/a /$sendfs/.zfs/snapshot/a - fi - if [[ -d /$recvfs/.zfs/snapshot/b && -d \ - /$sendfs/.zfs/snapshot/b ]]; then - log_must directory_diff /$recvfs/.zfs/snapshot/b /$sendfs/.zfs/snapshot/b + # + # The datasets are often not mounted at this point (the receive + # side is usually created with -u), and the .zfs directories the + # diffs need only exist once they are mounted. + # + ismounted $sendfs || log_must zfs mount $sendfs + ismounted $recvfs || log_must zfs mount $recvfs + + # + # Only compare the snapshots both sides have; some tests send + # just one of them. Comparing nothing means the caller's + # expectations are wrong, and historically this function did + # exactly that whenever the receive side was unmounted. + # + typeset snap + for snap in a b; do + [[ -d /$sendfs/.zfs/snapshot/$snap && \ + -d /$recvfs/.zfs/snapshot/$snap ]] || continue + log_must directory_diff /$recvfs/.zfs/snapshot/$snap \ + /$sendfs/.zfs/snapshot/$snap + compared=$((compared + 1)) + done + + if [[ $compared -eq 0 ]]; then + log_fail "file_check: no snapshots compared for" \ + "$sendfs and $recvfs" fi } diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend_024_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend_024_pos.ksh index f5c954043676..82fa0af5881c 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend_024_pos.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/rsend_024_pos.ksh @@ -48,7 +48,12 @@ log_onexit resume_cleanup $sendfs $streamfs test_fs_setup $sendfs $recvfs $streamfs log_must zfs unmount -f $sendfs resume_test "zfs send $sendfs" $streamfs $recvfs 0 -file_check $sendfs $recvfs + +# The stream was taken from the head, not from the a/b snapshots +# file_check compares, so diff the mounted heads instead +log_must zfs mount $sendfs +log_must zfs mount $recvfs +log_must directory_diff /$sendfs /$recvfs log_pass "Verify resumability of a full ZFS send/receive with the source " \ "filesystem unmounted" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_incremental.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_incremental.ksh index ca76aca1faed..de82373ec865 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_incremental.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_incremental.ksh @@ -61,7 +61,8 @@ typeset second=$POOL2/second log_onexit cleanup # Allow micro ZAPs to grow beyond SPA_OLD_MAXBLOCKSIZE. -set_tunable64 ZAP_MICRO_MAX_SIZE 1048576 +log_must save_tunable ZAP_MICRO_MAX_SIZE +log_must set_tunable64 ZAP_MICRO_MAX_SIZE 1048576 # Create a dataset with a large recordsize (1MB) log_must zfs create -o recordsize=1M $src diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_transitive.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_transitive.ksh index f44ba49660bc..412033ade44c 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_transitive.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_large_microzap_transitive.ksh @@ -65,7 +65,8 @@ typeset third=$POOL3/third log_onexit cleanup # Allow micro ZAPs to grow beyond SPA_OLD_MAXBLOCKSIZE. -set_tunable64 ZAP_MICRO_MAX_SIZE 1048576 +log_must save_tunable ZAP_MICRO_MAX_SIZE +log_must set_tunable64 ZAP_MICRO_MAX_SIZE 1048576 # Ensure the third pool exists. datasetexists $POOL3 || log_must zpool create $POOL3 $DISK3 diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh index 34b2c0a9b493..df7ecb8ef609 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_partial_dataset.ksh @@ -70,7 +70,9 @@ log_mustnot eval "zfs send --saved $POOL/recvfullfs | zfs recv -s " \ "$POOL/partialfs" token=$(zfs get -Hp -o value receive_resume_token $POOL/partialfs) log_must eval "zfs send -t $token | zfs recv -s $POOL/partialfs" -file_check $POOL/recvfullfs $POOL/partialfs +# recvfullfs never completes and has no snapshots to compare, so +# check the result against the dataset the stream originally came from +file_check $POOL/testfs2 $POOL/partialfs log_must zfs destroy -r $POOL/partialfs # Perform saved send with incremental diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_split_large_block.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_split_large_block.ksh new file mode 100755 index 000000000000..688f71010503 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/rsend/send_split_large_block.ksh @@ -0,0 +1,144 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2026 by MorganaFuture. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/rsend/rsend.kshlib + +# +# Description: +# A dataset with a large recordsize can hold a single-block file whose block +# size is not a power of two. Sending it without large blocks (no -L) splits +# that block into SPA_OLD_MAXBLOCKSIZE (128K) chunks, and the final chunk is +# smaller than the block size. The receiver must accept such a stream, for +# plain, compressed, initial and incremental sends, and reproduce the file +# exactly. Splitting of multi-block objects and a follow-up large-block (-L) +# incremental that rewrites the object are covered too. +# +# Previously the receiver assumed a size-mismatched WRITE record was always +# larger than the block size, so this shorter trailing chunk tripped a bad +# assertion and hung the receive_writer thread on debug builds (#17829). +# + +verify_runnable "both" + +typeset SPLIT=131072 # SPA_OLD_MAXBLOCKSIZE, the no-large-block split size + +function cleanup +{ + rm -f $BACKDIR/fs@* $BACKDIR/fsmulti@* $BACKDIR/stream* + for ds in fs fsmulti recv recvc recvmulti; do + datasetexists $POOL/$ds && destroy_dataset $POOL/$ds "-rR" + done +} + +# +# Extract the logical_size of every WRITE record in a stream, one per line. +# +function write_sizes # stream +{ + zstream dump -v "$1" | awk '/^WRITE object/ { + for (i = 1; i <= NF; i++) + if ($i == "logical_size") + print $(i + 2) + }' +} + +# +# Assert the stream split a large block into full 128K chunks plus a shorter +# trailing chunk (the case that used to trip the assertion). +# +function assert_split_with_tail # stream +{ + typeset full=0 tail=0 s + for s in $(write_sizes "$1"); do + if [[ $s -eq $SPLIT ]]; then + full=$((full + 1)) + elif [[ $s -gt 0 && $s -lt $SPLIT ]]; then + tail=$((tail + 1)) + fi + done + log_note "$1: 128K WRITEs=$full short-tail WRITEs=$tail" + [[ $full -ge 1 ]] || log_fail "$1: expected full 128K WRITE records" + [[ $tail -ge 1 ]] || log_fail "$1: expected a short trailing WRITE record" +} + +log_assert "A non-large-block send of a large non-power-of-2 block is received intact" +log_onexit cleanup + +log_must zfs create -o recordsize=1m $POOL/fs + +# 300000 bytes in a recordsize=1m dataset is stored in a single block whose +# size is rounded up to 300032 bytes (293K): larger than 128K and not a power +# of two, so a send without -L must emit a short trailing 128K-split chunk. +log_must dd if=/dev/urandom of=/$POOL/fs/file bs=1000 count=300 +log_must zfs snapshot $POOL/fs@snap1 + +# Guard the premise: were the block ever a power of two the split would be +# even and would no longer exercise the short-tail path this test targets. +typeset dblk=$(zdb -dddd $POOL/fs 2>/dev/null | awk '/ZFS plain file/ {print $4}') +log_must test "$dblk" = "293K" + +# Initial send without -L: verify via zstream dump that the block really was +# split into 128K chunks with a shorter trailing chunk, then that the receive +# reproduces the file byte-for-byte. +log_must eval "zfs send $POOL/fs@snap1 >$BACKDIR/stream.snap1" +assert_split_with_tail "$BACKDIR/stream.snap1" +log_must eval "zfs recv $POOL/recv <$BACKDIR/stream.snap1" +log_must diff /$POOL/fs/file /$POOL/recv/file + +# Same, but with a compressed stream. +log_must eval "zfs send -c $POOL/fs@snap1 >$BACKDIR/stream.snap1c" +assert_split_with_tail "$BACKDIR/stream.snap1c" +log_must eval "zfs recv $POOL/recvc <$BACKDIR/stream.snap1c" +log_must diff /$POOL/fs/file /$POOL/recvc/file + +# Incremental send without -L: rewrite part of the split block and send the +# delta, both plain and compressed, on top of the initial receives. +log_must dd if=/dev/urandom of=/$POOL/fs/file bs=1000 count=100 seek=100 conv=notrunc +log_must zfs snapshot $POOL/fs@snap2 +log_must eval "zfs send -i @snap1 $POOL/fs@snap2 >$BACKDIR/stream.snap2" +log_must eval "zfs recv -F $POOL/recv <$BACKDIR/stream.snap2" +log_must diff /$POOL/fs/file /$POOL/recv/file + +log_must eval "zfs send -c -i @snap1 $POOL/fs@snap2 >$BACKDIR/stream.snap2c" +log_must eval "zfs recv -F $POOL/recvc <$BACKDIR/stream.snap2c" +log_must diff /$POOL/fs/file /$POOL/recvc/file + +# Follow-up incremental *with* large blocks: rewrite the whole file and send it +# as a single large block on top of the split representation already received. +# This exercises rewriting an object that was received in split form. +log_must dd if=/dev/urandom of=/$POOL/fs/file bs=1000 count=300 +log_must zfs snapshot $POOL/fs@snap3 +log_must eval "zfs send -L -i @snap2 $POOL/fs@snap3 >$BACKDIR/stream.snap3L" +log_must eval "zfs recv -F $POOL/recv <$BACKDIR/stream.snap3L" +log_must diff /$POOL/fs/file /$POOL/recv/file + +# Multi-block object: a file spanning several 1m blocks is split into many +# 128K chunks across blocks. Verify the batched split records are received +# and reproduce the file exactly. +log_must zfs create -o recordsize=1m $POOL/fsmulti +log_must dd if=/dev/urandom of=/$POOL/fsmulti/file bs=1024k count=3 +log_must zfs snapshot $POOL/fsmulti@snap1 +log_must eval "zfs send $POOL/fsmulti@snap1 >$BACKDIR/stream.multi" +typeset nsplit=$(write_sizes "$BACKDIR/stream.multi" | grep -c "^$SPLIT$") +log_note "multi-block stream: 128K WRITEs=$nsplit" +[[ $nsplit -ge 16 ]] || log_fail "expected many 128K WRITE records for a multi-block object" +log_must eval "zfs recv $POOL/recvmulti <$BACKDIR/stream.multi" +log_must diff /$POOL/fsmulti/file /$POOL/recvmulti/file + +log_pass "A non-large-block send of a large non-power-of-2 block is received intact" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/stat/statx_dioalign.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/stat/statx_dioalign.ksh index ab749b5f793a..ea10e4925039 100755 --- a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/stat/statx_dioalign.ksh +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/stat/statx_dioalign.ksh @@ -89,7 +89,8 @@ typeset -i PAGE_SIZE=$(getconf PAGE_SIZE) # Set recordsize to 128K, and make a 64K file (so only one block) for the # sizing tests below. log_must zfs set recordsize=128K $TESTDS -log_must dd if=/dev/urandom of=$TESTFILE bs=64k count=1 +log_must rm -f $TESTFILE +log_must stride_dd -i /dev/urandom -o $TESTFILE -b 65536 -c 1 log_must zpool sync # when DIO is disabled via tunable, statx will not return the dioalign result @@ -141,7 +142,7 @@ done # Now we extend the file into its second block. This effectively locks in its # block size, which will always be returned regardless of recordsize changes. log_must zfs set recordsize=128K $TESTDS -log_must dd if=/dev/urandom of=$TESTFILE bs=192K count=1 +log_must stride_dd -i /dev/urandom -o $TESTFILE -b 196608 -c 1 log_must zpool sync # Confirm that no matter how we change the recordsize, the alignment remains at @@ -167,14 +168,14 @@ log_must rm -f $TESTFILE log_must touch $TESTFILE log_must zpool sync assert_dioalign $TESTFILE $PAGE_SIZE 16384 -log_must dd if=/dev/urandom of=$TESTFILE bs=16384 count=16 oflag=direct +log_must stride_dd -i /dev/urandom -o $TESTFILE -b 16384 -c 16 -D # same again, but writing with incorrect alignment, which should fail. log_must rm -f $TESTFILE log_must touch $TESTFILE log_must zpool sync assert_dioalign $TESTFILE $PAGE_SIZE 16384 -log_mustnot dd if=/dev/urandom of=$TESTFILE bs=1024 count=256 oflag=direct +log_mustnot stride_dd -i /dev/urandom -o $TESTFILE -b 1024 -c 256 -D # same again, but without strict, which should succeed. log_must set_tunable32 DIO_STRICT 0 @@ -182,6 +183,6 @@ log_must rm -f $TESTFILE log_must touch $TESTFILE log_must zpool sync assert_dioalign $TESTFILE $PAGE_SIZE 16384 -log_must dd if=/dev/urandom of=$TESTFILE bs=1024 count=256 oflag=direct +log_must stride_dd -i /dev/urandom -o $TESTFILE -b 1024 -c 256 -D log_pass $CLAIM diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/user_namespace/user_namespace_secpolicy_sys_config.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/user_namespace/user_namespace_secpolicy_sys_config.ksh new file mode 100755 index 000000000000..c437a6d438ce --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/user_namespace/user_namespace_secpolicy_sys_config.ksh @@ -0,0 +1,46 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/tests/functional/user_namespace/user_namespace_common.kshlib + +# +# Verify that secpolicy_sys_config() only allows the superuser in the root/init +# namespace. +# +# Currently, zpool/zfs start by getting a list of pools, which has separate +# zone visibility checks, and then don't proceed if no pools are found. So, +# we use `zpool events` for the test, which uses secpolicy_sys_config() but +# doesn't need a name to work, and so zpool doesn't bother with the lookup. +# + +verify_runnable "both" + +log_assert "secpolicy_sys_config correctly limits access." + +# default superuser can access pool events +log_must eval 'zpool events >/dev/null' + +# regular user cannot access pool events +log_mustnot eval 'sudo -u nobody zpool events >/dev/null' + +# superuser in a new user namespace cannot access pool events +log_mustnot eval 'unshare -Ur zpool events >/dev/null' + +# regular user in a new user namespace cannot access pool events +log_mustnot eval 'unshare -U zpool events >/dev/null' + +log_pass "secpolicy_sys_config correctly limits access." diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/user_namespace/user_namespace_secpolicy_zinject.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/user_namespace/user_namespace_secpolicy_zinject.ksh new file mode 100755 index 000000000000..f02824f0e35b --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/user_namespace/user_namespace_secpolicy_zinject.ksh @@ -0,0 +1,46 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# https://opensource.org/license/CDDL-1.0. +# + +# +# Copyright (c) 2026, TrueNAS. +# + +. $STF_SUITE/tests/functional/user_namespace/user_namespace_common.kshlib + +# +# Verify that secpolicy_zinject() only allows the superuser in the root/init +# namespace. +# +# Because we don't actually want to inject anything, we just try to run +# `zinject -c all`, which with no injections is just a permission check and +# then an (effective) no-op. +# + +verify_runnable "both" + +log_assert "secpolicy_zinject correctly limits access." + +# default superuser can access pool events +log_must zinject -c all + +# regular user cannot access pool events +log_mustnot sudo -u nobody zinject -c all + +# superuser in a new user namespace cannot access pool events +log_mustnot unshare -Ur zinject -c all + +# regular user in a new user namespace cannot access pool events +log_mustnot unshare -U zinject -c all + +log_pass "secpolicy_zinject correctly limits access." + diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/userquota/groupspace_005_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/userquota/groupspace_005_pos.ksh new file mode 100755 index 000000000000..1955ae051237 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/userquota/groupspace_005_pos.ksh @@ -0,0 +1,100 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026, Gluesys. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +# +# DESCRIPTION: +# Verify 'zfs groupspace' reports correct quota values whether the +# dataset is mounted or not, and does not fail with EBUSY when +# raced against 'zfs mount'. +# +# STRATEGY: +# 1. Create a dataset with a groupquota and write 50M of data. +# 2. Check quota and used values while mounted. +# 3. Unmount and verify the same values are still readable. +# 4. Remount and re-verify values are consistent. +# 5. Race 'zfs groupspace' against 'zfs mount' 50 times; expect no EBUSY. +# + +function cleanup +{ + zfs unmount $RACE_FS 2>/dev/null + datasetexists $RACE_FS && destroy_dataset $RACE_FS + rm -f "$ERRFILE" "$DATAFILE" +} + +log_onexit cleanup + +typeset RACE_FS=$QFS/groupspace_race +typeset ERRFILE=/tmp/groupspace_005_err.$$ +typeset DATAFILE +typeset -i ITERS=50 + +log_assert "zfs groupspace shows correct quota values and does not EBUSY on mount race" + +log_must zfs create $RACE_FS +log_must zfs set groupquota@$QGROUP=100m $RACE_FS +mkmount_writable $RACE_FS +DATAFILE=$(get_prop mountpoint $RACE_FS)/groupspace_005_data.$$ +log_must user_run $QUSER1 mkfile 50m $DATAFILE +sync_all_pools + +log_note "check groupspace values while mounted" +log_must eval "zfs groupspace $RACE_FS | grep $QGROUP | grep 100M" +log_must eval "zfs groupspace $RACE_FS | grep $QGROUP | grep '50\\..*M'" + +log_must zfs unmount $RACE_FS +log_note "check groupspace values while unmounted" +log_must eval "zfs groupspace $RACE_FS | grep $QGROUP | grep 100M" +log_must eval "zfs groupspace $RACE_FS | grep $QGROUP | grep '50\\..*M'" + +log_must zfs mount $RACE_FS +log_note "check groupspace values after remount" +log_must eval "zfs groupspace $RACE_FS | grep $QGROUP | grep 100M" +log_must eval "zfs groupspace $RACE_FS | grep $QGROUP | grep '50\\..*M'" + +typeset -i i=0 ebusy=0 +while (( i < ITERS )); do + log_must zfs unmount $RACE_FS + zfs mount $RACE_FS & + typeset mpid=$! + zfs groupspace $RACE_FS >"$ERRFILE" 2>&1 + typeset rc=$? + wait $mpid + if (( rc != 0 )) && grep -qi "busy" "$ERRFILE" 2>/dev/null; then + (( ebusy++ )) + log_note "Iteration $i: EBUSY: $(cat $ERRFILE)" + fi + rm -f "$ERRFILE" + (( i++ )) +done + +(( ebusy > 0 )) && log_fail "EBUSY seen $ebusy/$ITERS times" + +log_pass "zfs groupspace shows correct quota values and does not EBUSY on mount race" diff --git a/sys/contrib/openzfs/tests/zfs-tests/tests/functional/userquota/userspace_005_pos.ksh b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/userquota/userspace_005_pos.ksh new file mode 100755 index 000000000000..fd98ded923a5 --- /dev/null +++ b/sys/contrib/openzfs/tests/zfs-tests/tests/functional/userquota/userspace_005_pos.ksh @@ -0,0 +1,100 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2026, Gluesys. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +# +# DESCRIPTION: +# Verify 'zfs userspace' reports correct quota values whether the +# dataset is mounted or not, and does not fail with EBUSY when +# raced against 'zfs mount'. +# +# STRATEGY: +# 1. Create a dataset with a userquota and write 50M of data. +# 2. Check quota and used values while mounted. +# 3. Unmount and verify the same values are still readable. +# 4. Remount and re-verify values are consistent. +# 5. Race 'zfs userspace' against 'zfs mount' 50 times; expect no EBUSY. +# + +function cleanup +{ + zfs unmount $RACE_FS 2>/dev/null + datasetexists $RACE_FS && destroy_dataset $RACE_FS + rm -f "$ERRFILE" "$DATAFILE" +} + +log_onexit cleanup + +typeset RACE_FS=$QFS/userspace_race +typeset ERRFILE=/tmp/userspace_005_err.$$ +typeset DATAFILE +typeset -i ITERS=50 + +log_assert "zfs userspace shows correct quota values and does not EBUSY on mount race" + +log_must zfs create $RACE_FS +log_must zfs set userquota@$QUSER1=100m $RACE_FS +mkmount_writable $RACE_FS +DATAFILE=$(get_prop mountpoint $RACE_FS)/userspace_005_data.$$ +log_must user_run $QUSER1 mkfile 50m $DATAFILE +sync_all_pools + +log_note "check userspace values while mounted" +log_must eval "zfs userspace $RACE_FS | grep $QUSER1 | grep 100M" +log_must eval "zfs userspace $RACE_FS | grep $QUSER1 | grep '50\\..*M'" + +log_must zfs unmount $RACE_FS +log_note "check userspace values while unmounted" +log_must eval "zfs userspace $RACE_FS | grep $QUSER1 | grep 100M" +log_must eval "zfs userspace $RACE_FS | grep $QUSER1 | grep '50\\..*M'" + +log_must zfs mount $RACE_FS +log_note "check userspace values after remount" +log_must eval "zfs userspace $RACE_FS | grep $QUSER1 | grep 100M" +log_must eval "zfs userspace $RACE_FS | grep $QUSER1 | grep '50\\..*M'" + +typeset -i i=0 ebusy=0 +while (( i < ITERS )); do + log_must zfs unmount $RACE_FS + zfs mount $RACE_FS & + typeset mpid=$! + zfs userspace $RACE_FS >"$ERRFILE" 2>&1 + typeset rc=$? + wait $mpid + if (( rc != 0 )) && grep -qi "busy" "$ERRFILE" 2>/dev/null; then + (( ebusy++ )) + log_note "Iteration $i: EBUSY: $(cat $ERRFILE)" + fi + rm -f "$ERRFILE" + (( i++ )) +done + +(( ebusy > 0 )) && log_fail "EBUSY seen $ebusy/$ITERS times" + +log_pass "zfs userspace shows correct quota values and does not EBUSY on mount race" diff --git a/sys/module/zfs/zfs_config.h b/sys/module/zfs/zfs_config.h new file mode 100644 index 000000000000..aa562fa08af7 --- /dev/null +++ b/sys/module/zfs/zfs_config.h @@ -0,0 +1,946 @@ +/* + */ + +/* zfs_config.h. Generated from zfs_config.h.in by configure. */ +/* zfs_config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +/* #undef ENABLE_NLS */ + +/* __assign_str() has one arg */ +/* #undef HAVE_1ARG_ASSIGN_STR */ + +/* lookup_bdev() wants 1 arg */ +/* #undef HAVE_1ARG_LOOKUP_BDEV */ + +/* kernel has access_ok with 'type' parameter */ +/* #undef HAVE_ACCESS_OK_TYPE */ + +/* add_disk() returns int */ +/* #undef HAVE_ADD_DISK_RET */ + +/* Define if host toolchain supports AES */ +#define HAVE_AES 1 + +/* Define if you have [rt] */ +#define HAVE_AIO_H 1 + +/* Define if your assembler supports .cfi_negate_ra_state. */ +/* #undef HAVE_AS_CFI_PSEUDO_OP */ + +#ifdef __amd64__ +#ifndef RESCUE +/* Define if host toolchain supports AVX */ +#define HAVE_AVX 1 +#endif + +/* Define if host toolchain supports AVX2 */ +#define HAVE_AVX2 1 + +/* Define if host toolchain supports AVX512BW */ +#define HAVE_AVX512BW 1 + +/* Define if host toolchain supports AVX512CD */ +#define HAVE_AVX512CD 1 + +/* Define if host toolchain supports AVX512DQ */ +#define HAVE_AVX512DQ 1 + +/* Define if host toolchain supports AVX512ER */ +#define HAVE_AVX512ER 1 + +/* Define if host toolchain supports AVX512F */ +#define HAVE_AVX512F 1 + +/* Define if host toolchain supports AVX512IFMA */ +#define HAVE_AVX512IFMA 1 + +/* Define if host toolchain supports AVX512PF */ +#define HAVE_AVX512PF 1 + +/* Define if host toolchain supports AVX512VBMI */ +#define HAVE_AVX512VBMI 1 + +/* Define if host toolchain supports AVX512VL */ +#define HAVE_AVX512VL 1 +#endif + +/* backtrace() is available */ +/* #undef HAVE_BACKTRACE */ + +/* bdevname() is available */ +/* #undef HAVE_BDEVNAME */ + +/* bdev_check_media_change() exists */ +/* #undef HAVE_BDEV_CHECK_MEDIA_CHANGE */ + +/* bdev_file_open_by_path() exists */ +/* #undef HAVE_BDEV_FILE_OPEN_BY_PATH */ + +/* bdev_*_io_acct() available */ +/* #undef HAVE_BDEV_IO_ACCT_63 */ + +/* bdev_*_io_acct() available */ +/* #undef HAVE_BDEV_IO_ACCT_OLD */ + +/* bdev_kobj() exists */ +/* #undef HAVE_BDEV_KOBJ */ + +/* bdev_max_discard_sectors() is available */ +/* #undef HAVE_BDEV_MAX_DISCARD_SECTORS */ + +/* bdev_max_secure_erase_sectors() is available */ +/* #undef HAVE_BDEV_MAX_SECURE_ERASE_SECTORS */ + +/* bdev_nr_bytes() is available */ +/* #undef HAVE_BDEV_NR_BYTES */ + +/* bdev_open_by_path() exists */ +/* #undef HAVE_BDEV_OPEN_BY_PATH */ + +/* bdev_release() exists */ +/* #undef HAVE_BDEV_RELEASE */ + +/* block_device_operations->submit_bio() returns void */ +/* #undef HAVE_BDEV_SUBMIT_BIO_RETURNS_VOID */ + +/* bdev_whole() is available */ +/* #undef HAVE_BDEV_WHOLE */ + +/* bio_alloc() takes 4 arguments */ +/* #undef HAVE_BIO_ALLOC_4ARG */ + +/* bio->bi_bdev->bd_disk exists */ +/* #undef HAVE_BIO_BDEV_DISK */ + +/* bio_*_io_acct() available */ +/* #undef HAVE_BIO_IO_ACCT */ + +/* bio_max_segs() is implemented */ +/* #undef HAVE_BIO_MAX_SEGS */ + +/* bio_set_dev() GPL-only */ +/* #undef HAVE_BIO_SET_DEV_GPL_ONLY */ + +/* bio_set_dev() is a macro */ +/* #undef HAVE_BIO_SET_DEV_MACRO */ + +/* bio_set_op_attrs is available */ +/* #undef HAVE_BIO_SET_OP_ATTRS */ + +/* blkdev_get_by_path() exists and takes 4 args */ +/* #undef HAVE_BLKDEV_GET_BY_PATH_4ARG */ + +/* blkdev_get_by_path() handles ERESTARTSYS */ +/* #undef HAVE_BLKDEV_GET_ERESTARTSYS */ + +/* __blkdev_issue_discard(flags) is available */ +/* #undef HAVE_BLKDEV_ISSUE_DISCARD_ASYNC_FLAGS */ + +/* __blkdev_issue_discard() is available */ +/* #undef HAVE_BLKDEV_ISSUE_DISCARD_ASYNC_NOFLAGS */ + +/* blkdev_issue_discard(flags) is available */ +/* #undef HAVE_BLKDEV_ISSUE_DISCARD_FLAGS */ + +/* blkdev_issue_discard() is available */ +/* #undef HAVE_BLKDEV_ISSUE_DISCARD_NOFLAGS */ + +/* blkdev_issue_secure_erase() is available */ +/* #undef HAVE_BLKDEV_ISSUE_SECURE_ERASE */ + +/* blkdev_put() exists */ +/* #undef HAVE_BLKDEV_PUT */ + +/* blkdev_put() accepts void* as arg 2 */ +/* #undef HAVE_BLKDEV_PUT_HOLDER */ + +/* struct queue_limits has a features field */ +/* #undef HAVE_BLKDEV_QUEUE_LIMITS_FEATURES */ + +/* blkdev_reread_part() exists */ +/* #undef HAVE_BLKDEV_REREAD_PART */ + +/* blkg_tryget() is available */ +/* #undef HAVE_BLKG_TRYGET */ + +/* blkg_tryget() GPL-only */ +/* #undef HAVE_BLKG_TRYGET_GPL_ONLY */ + +/* blk_alloc_disk() exists */ +/* #undef HAVE_BLK_ALLOC_DISK */ + +/* blk_alloc_disk() exists and takes 2 args */ +/* #undef HAVE_BLK_ALLOC_DISK_2ARG */ + +/* blk_alloc_queue() expects request function */ +/* #undef HAVE_BLK_ALLOC_QUEUE_REQUEST_FN */ + +/* blk_alloc_queue_rh() expects request function */ +/* #undef HAVE_BLK_ALLOC_QUEUE_REQUEST_FN_RH */ + +/* blk_cleanup_disk() exists */ +/* #undef HAVE_BLK_CLEANUP_DISK */ + +/* blk_mode_t is defined */ +/* #undef HAVE_BLK_MODE_T */ + +/* block multiqueue hardware context is cached in struct request */ +/* #undef HAVE_BLK_MQ_RQ_HCTX */ + +/* blk queue backing_dev_info is dynamic */ +/* #undef HAVE_BLK_QUEUE_BDI_DYNAMIC */ + +/* blk_queue_discard() is available */ +/* #undef HAVE_BLK_QUEUE_DISCARD */ + +/* backing_dev_info is available through queue gendisk */ +/* #undef HAVE_BLK_QUEUE_DISK_BDI */ + +/* blk_queue_rot() is available */ +/* #undef HAVE_BLK_QUEUE_ROT */ + +/* blk_queue_secure_erase() is available */ +/* #undef HAVE_BLK_QUEUE_SECURE_ERASE */ + +/* blk_queue_update_readahead() exists */ +/* #undef HAVE_BLK_QUEUE_UPDATE_READAHEAD */ + +/* BLK_STS_RESV_CONFLICT is defined */ +/* #undef HAVE_BLK_STS_RESV_CONFLICT */ + +/* Define if getgeo() in block_device_operations takes struct gendisk * as its + first arg */ +/* #undef HAVE_BLOCK_DEVICE_OPERATIONS_GETGEO_GENDISK */ + +/* Define if release() in block_device_operations takes 1 arg */ +/* #undef HAVE_BLOCK_DEVICE_OPERATIONS_RELEASE_1ARG */ + +/* Define if revalidate_disk() in block_device_operations */ +/* #undef HAVE_BLOCK_DEVICE_OPERATIONS_REVALIDATE_DISK */ + +/* Define to 1 if you have the Mac OS X function + CFLocaleCopyPreferredLanguages in the CoreFoundation framework. */ +/* #undef HAVE_CFLOCALECOPYPREFERREDLANGUAGES */ + +/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ + +/* check_disk_change() exists */ +/* #undef HAVE_CHECK_DISK_CHANGE */ + +/* copy_splice_read exists */ +/* #undef HAVE_COPY_SPLICE_READ */ + +/* cpu_has_feature() is GPL-only */ +/* #undef HAVE_CPU_HAS_FEATURE_GPL_ONLY */ + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +/* #undef HAVE_DCGETTEXT */ + +/* DECLARE_EVENT_CLASS() is available */ +/* #undef HAVE_DECLARE_EVENT_CLASS */ + +/* dentry aliases are in d_u member */ +/* #undef HAVE_DENTRY_D_U_ALIASES */ + +/* 3-arg dequeue_signal() takes a type argument */ +/* #undef HAVE_DEQUEUE_SIGNAL_3ARG_TYPE */ + +/* dequeue_signal() takes 4 arguments */ +/* #undef HAVE_DEQUEUE_SIGNAL_4ARG */ + +/* lookup_bdev() wants dev_t arg */ +/* #undef HAVE_DEVT_LOOKUP_BDEV */ + +/* disk_check_media_change() exists */ +/* #undef HAVE_DISK_CHECK_MEDIA_CHANGE */ + +/* disk_*_io_acct() available */ +/* #undef HAVE_DISK_IO_ACCT */ + +/* disk_update_readahead() exists */ +/* #undef HAVE_DISK_UPDATE_READAHEAD */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* dops->d_revalidate() takes 4 args */ +/* #undef HAVE_D_REVALIDATE_4ARGS */ + +/* Define if d_set_d_op() is available */ +/* #undef HAVE_D_SET_D_OP */ + +/* Define to 1 if you have the 'execvpe' function. */ +#define HAVE_EXECVPE 1 + +/* fault_in_iov_iter_readable() is available */ +/* #undef HAVE_FAULT_IN_IOV_ITER_READABLE */ + +/* linux/filelock.h exists */ +/* #undef HAVE_FILELOCK_HEADER */ + +/* file->f_version exists */ +/* #undef HAVE_FILE_F_VERSION */ + +/* flush_dcache_page() is GPL-only */ +/* #undef HAVE_FLUSH_DCACHE_PAGE_GPL_ONLY */ + +/* use __flush_workqueue() to flush delay workqueue */ +/* #undef HAVE_FLUSH_DELAY_WORKQUEUE_INTERNAL */ + +/* use system_delay_wq for delay workqueue */ +/* #undef HAVE_FLUSH_DELAY_WORKQUEUE_PERCPU */ + +/* follow_down() takes a flags parameter */ +/* #undef HAVE_FOLLOW_DOWN_FLAGS */ + +/* Define if compiler supports -Wformat-overflow */ +/* #undef HAVE_FORMAT_OVERFLOW */ + +/* fsync_bdev() is declared in include/blkdev.h */ +/* #undef HAVE_FSYNC_BDEV */ + +/* fs_context exists */ +/* #undef HAVE_FS_CONTEXT */ + +/* fs_parse() takes fs_parameter_spec directly */ +/* #undef HAVE_FS_PARSE_TAKES_SPEC */ + +/* yes */ +/* #undef HAVE_GENERIC_FADVISE */ + +/* generic_fillattr requires struct mnt_idmap* */ +/* #undef HAVE_GENERIC_FILLATTR_IDMAP */ + +/* generic_fillattr requires struct mnt_idmap* and u32 request_mask */ +/* #undef HAVE_GENERIC_FILLATTR_IDMAP_REQMASK */ + +/* generic_fillattr requires struct user_namespace* */ +/* #undef HAVE_GENERIC_FILLATTR_USERNS */ + +/* generic_*_io_acct() 4 arg available */ +/* #undef HAVE_GENERIC_IO_ACCT_4ARG */ + +/* GENHD_FL_EXT_DEVT flag is available */ +/* #undef HAVE_GENHD_FL_EXT_DEVT */ + +/* GENHD_FL_NO_PART flag is available */ +/* #undef HAVE_GENHD_FL_NO_PART */ + +/* Define if the GNU gettext() function is already present or preinstalled. */ +/* #undef HAVE_GETTEXT */ + +/* Define to 1 if you have the 'gettid' function. */ +/* #undef HAVE_GETTID */ + +/* iops->get_acl() exists */ +/* #undef HAVE_GET_ACL */ + +/* iops->get_acl() takes rcu */ +/* #undef HAVE_GET_ACL_RCU */ + +/* has iops->get_inode_acl() */ +/* #undef HAVE_GET_INODE_ACL */ + +/* iattr->ia_vfsuid and iattr->ia_vfsgid exist */ +/* #undef HAVE_IATTR_VFSID */ + +/* Define if you have the iconv() function and it works. */ +#define HAVE_ICONV 1 + +/* iops->getattr() takes struct mnt_idmap* */ +/* #undef HAVE_IDMAP_IOPS_GETATTR */ + +/* iops->setattr() takes struct mnt_idmap* */ +/* #undef HAVE_IDMAP_IOPS_SETATTR */ + +/* id mapping mechanism is mnt_idmap */ +/* #undef HAVE_IDMAP_MNTIDMAP */ + +/* APIs for idmapped mount are present */ +/* #undef HAVE_IDMAP_MNT_API */ + +/* mnt_idmap does not have user_namespace */ +/* #undef HAVE_IDMAP_NO_USERNS */ + +/* id mapping mechanism is user_namespace */ +/* #undef HAVE_IDMAP_USERNS */ + +/* Define if compiler supports -Wimplicit-fallthrough */ +/* #undef HAVE_IMPLICIT_FALLTHROUGH */ + +/* Define if compiler supports -Winfinite-recursion */ +/* #undef HAVE_INFINITE_RECURSION */ + +/* inode_generic_drop() exists */ +/* #undef HAVE_INODE_GENERIC_DROP */ + +/* inode_get_atime() exists in linux/fs.h */ +/* #undef HAVE_INODE_GET_ATIME */ + +/* inode_get_ctime() exists in linux/fs.h */ +/* #undef HAVE_INODE_GET_CTIME */ + +/* inode_get_mtime() exists in linux/fs.h */ +/* #undef HAVE_INODE_GET_MTIME */ + +/* inode_owner_or_capable() exists */ +/* #undef HAVE_INODE_OWNER_OR_CAPABLE */ + +/* inode_owner_or_capable() takes mnt_idmap */ +/* #undef HAVE_INODE_OWNER_OR_CAPABLE_IDMAP */ + +/* inode_owner_or_capable() takes user_ns */ +/* #undef HAVE_INODE_OWNER_OR_CAPABLE_USERNS */ + +/* inode_set_atime_to_ts() exists in linux/fs.h */ +/* #undef HAVE_INODE_SET_ATIME_TO_TS */ + +/* inode_set_ctime_to_ts() exists in linux/fs.h */ +/* #undef HAVE_INODE_SET_CTIME_TO_TS */ + +/* inode_set_mtime_to_ts() exists in linux/fs.h */ +/* #undef HAVE_INODE_SET_MTIME_TO_TS */ + +/* inode_state_read_once() exists */ +/* #undef HAVE_INODE_STATE_READ_ONCE */ + +/* timestamp_truncate() exists */ +/* #undef HAVE_INODE_TIMESTAMP_TRUNCATE */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* iops->create() takes struct mnt_idmap* */ +/* #undef HAVE_IOPS_CREATE_IDMAP */ + +/* iops->create() takes struct user_namespace* */ +/* #undef HAVE_IOPS_CREATE_USERNS */ + +/* iops->mkdir() returns struct dentry* */ +/* #undef HAVE_IOPS_MKDIR_DENTRY */ + +/* iops->mkdir() takes struct mnt_idmap* */ +/* #undef HAVE_IOPS_MKDIR_IDMAP */ + +/* iops->mkdir() takes struct user_namespace* */ +/* #undef HAVE_IOPS_MKDIR_USERNS */ + +/* iops->mknod() takes struct mnt_idmap* */ +/* #undef HAVE_IOPS_MKNOD_IDMAP */ + +/* iops->mknod() takes struct user_namespace* */ +/* #undef HAVE_IOPS_MKNOD_USERNS */ + +/* iops->permission() takes struct mnt_idmap* */ +/* #undef HAVE_IOPS_PERMISSION_IDMAP */ + +/* iops->permission() takes struct user_namespace* */ +/* #undef HAVE_IOPS_PERMISSION_USERNS */ + +/* iops->rename() takes struct mnt_idmap* */ +/* #undef HAVE_IOPS_RENAME_IDMAP */ + +/* iops->rename() takes struct user_namespace* */ +/* #undef HAVE_IOPS_RENAME_USERNS */ + +/* iops->symlink() takes struct mnt_idmap* */ +/* #undef HAVE_IOPS_SYMLINK_IDMAP */ + +/* iops->symlink() takes struct user_namespace* */ +/* #undef HAVE_IOPS_SYMLINK_USERNS */ + +/* iov_iter_get_pages2() is available */ +/* #undef HAVE_IOV_ITER_GET_PAGES2 */ + +/* iov_iter_type() is available */ +/* #undef HAVE_IOV_ITER_TYPE */ + +/* Define to 1 if you have the 'issetugid' function. */ +#define HAVE_ISSETUGID 1 + +/* iter_iov() is available */ +/* #undef HAVE_ITER_IOV */ + +/* iter_is_ubuf() is available */ +/* #undef HAVE_ITER_IS_UBUF */ + +/* kasan_enabled() is GPL-only */ +/* #undef HAVE_KASAN_ENABLED_GPL_ONLY */ + +/* kernel has kernel_fpu_* functions */ +/* #undef HAVE_KERNEL_FPU */ + +/* kernel has asm/fpu/api.h */ +/* #undef HAVE_KERNEL_FPU_API_HEADER */ + +/* kernel fpu internal */ +/* #undef HAVE_KERNEL_FPU_INTERNAL */ + +/* kernel has asm/fpu/internal.h */ +/* #undef HAVE_KERNEL_FPU_INTERNAL_HEADER */ + +/* Define if compiler supports -Winfinite-recursion */ +/* #undef HAVE_KERNEL_INFINITE_RECURSION */ + +/* kernel defines intptr_t */ +/* #undef HAVE_KERNEL_INTPTR_T */ + +/* kernel has kernel_neon_* functions */ +/* #undef HAVE_KERNEL_NEON */ + +/* kernel does stack verification */ +/* #undef HAVE_KERNEL_OBJTOOL */ + +/* kernel has linux/objtool.h */ +/* #undef HAVE_KERNEL_OBJTOOL_HEADER */ + +/* strlcpy() exists */ +/* #undef HAVE_KERNEL_STRLCPY */ + +/* kernel has kmap_local_page */ +/* #undef HAVE_KMAP_LOCAL_PAGE */ + +/* Define if you have [aio] */ +/* #undef HAVE_LIBAIO */ + +/* Define if you have [blkid] */ +/* #undef HAVE_LIBBLKID */ + +/* Define if you have [crypto] */ +#define HAVE_LIBCRYPTO 1 + +/* Define if you have [tirpc] */ +/* #undef HAVE_LIBTIRPC */ + +/* Define if you have [udev] */ +/* #undef HAVE_LIBUDEV */ + +/* Define if you have [unwind] */ +/* #undef HAVE_LIBUNWIND */ + +/* libunwind has unw_get_elf_filename */ +/* #undef HAVE_LIBUNWIND_ELF */ + +/* Define if you have [uuid] */ +/* #undef HAVE_LIBUUID */ + +/* building against unsupported kernel version */ +/* #undef HAVE_LINUX_EXPERIMENTAL */ + +/* makedev() is declared in sys/mkdev.h */ +/* #undef HAVE_MAKEDEV_IN_MKDEV */ + +/* makedev() is declared in sys/sysmacros.h */ +/* #undef HAVE_MAKEDEV_IN_SYSMACROS */ + +/* Noting that make_request_fn() returns blk_qc_t */ +/* #undef HAVE_MAKE_REQUEST_FN_RET_QC */ + +/* Define to 1 if you have the 'mlockall' function. */ +#define HAVE_MLOCKALL 1 + +/* 'flags' in 'struct page' is a struct */ +/* #undef HAVE_MM_PAGE_FLAGS_STRUCT */ + +/* PG_error flag is available */ +/* #undef HAVE_MM_PAGE_FLAG_ERROR */ + +/* page_mapping() is available */ +/* #undef HAVE_MM_PAGE_MAPPING */ + +/* page_size() is available */ +/* #undef HAVE_MM_PAGE_SIZE */ + +/* mount_setattr() and struct mount_attr are available */ +/* #undef HAVE_MOUNT_SETATTR */ + +/* Define if host toolchain supports MOVBE */ +#define HAVE_MOVBE 1 + +/* Define if ns_type is accessible through ns_common */ +/* #undef HAVE_NS_COMMON_TYPE */ + +/* folio_wait_bit() exists */ +/* #undef HAVE_PAGEMAP_FOLIO_WAIT_BIT */ + +/* readahead_page() exists */ +/* #undef HAVE_PAGEMAP_READAHEAD_PAGE */ + +/* part_to_dev() exists */ +/* #undef HAVE_PART_TO_DEV */ + +/* iops->getattr() takes a path */ +/* #undef HAVE_PATH_IOPS_GETATTR */ + +/* Define if host toolchain supports PCLMULQDQ */ +#define HAVE_PCLMULQDQ 1 + +/* pin_user_pages_unlocked() is available */ +/* #undef HAVE_PIN_USER_PAGES_UNLOCKED */ + +/* posix_acl_to_xattr() allocates its result */ +/* #undef HAVE_POSIX_ACL_TO_XATTR_ALLOC */ + +/* proc_handler ctl_table arg is const */ +/* #undef HAVE_PROC_HANDLER_CTL_TABLE_CONST */ + +/* proc_ops structure exists */ +/* #undef HAVE_PROC_OPS_STRUCT */ + +/* If available, contains the Python version number currently in use. */ +/* #undef HAVE_PYTHON */ + +/* qat is enabled and existed */ +/* #undef HAVE_QAT */ + +/* struct reclaim_state has reclaimed */ +/* #undef HAVE_RECLAIM_STATE_RECLAIMED */ + +/* register_shrinker is vararg */ +/* #undef HAVE_REGISTER_SHRINKER_VARARG */ + +/* register_sysctl_sz exists */ +/* #undef HAVE_REGISTER_SYSCTL_SZ */ + +/* register_sysctl_table exists */ +/* #undef HAVE_REGISTER_SYSCTL_TABLE */ + +/* revalidate_disk() is available */ +/* #undef HAVE_REVALIDATE_DISK */ + +/* revalidate_disk_size() is available */ +/* #undef HAVE_REVALIDATE_DISK_SIZE */ + +/* Define to 1 if you have the <security/pam_modules.h> header file. */ +#define HAVE_SECURITY_PAM_MODULES_H 1 + +/* setattr_prepare() accepts mnt_idmap */ +/* #undef HAVE_SETATTR_PREPARE_IDMAP */ + +/* setattr_prepare() is available, doesn't accept user_namespace */ +/* #undef HAVE_SETATTR_PREPARE_NO_USERNS */ + +/* setattr_prepare() accepts user_namespace */ +/* #undef HAVE_SETATTR_PREPARE_USERNS */ + +/* iops->set_acl() takes 4 args, arg1 is struct mnt_idmap * */ +/* #undef HAVE_SET_ACL_IDMAP_DENTRY */ + +/* iops->set_acl() takes 4 args */ +/* #undef HAVE_SET_ACL_USERNS */ + +/* iops->set_acl() takes 4 args, arg2 is struct dentry * */ +/* #undef HAVE_SET_ACL_USERNS_DENTRY_ARG2 */ + +/* Define if set_default_d_op() is available */ +/* #undef HAVE_SET_DEFAULT_D_OP */ + +/* shrinker_register exists */ +/* #undef HAVE_SHRINKER_REGISTER */ + +/* kernel_siginfo_t exists */ +/* #undef HAVE_SIGINFO */ + +/* sops->free_inode() exists */ +/* #undef HAVE_SOPS_FREE_INODE */ + +#if defined(__amd64__) || defined(__i386__) +/* Define if host toolchain supports SSE */ +#define HAVE_SSE 1 + +/* Define if host toolchain supports SSE2 */ +#define HAVE_SSE2 1 + +/* Define if host toolchain supports SSE3 */ +#define HAVE_SSE3 1 + +/* Define if host toolchain supports SSE4.1 */ +#define HAVE_SSE4_1 1 + +/* Define if host toolchain supports SSE4.2 */ +#define HAVE_SSE4_2 1 + +/* Define if host toolchain supports SSSE3 */ +#define HAVE_SSSE3 1 +#endif + +/* STACK_FRAME_NON_STANDARD is defined */ +/* #undef HAVE_STACK_FRAME_NON_STANDARD */ + +/* STACK_FRAME_NON_STANDARD asm macro is defined */ +/* #undef HAVE_STACK_FRAME_NON_STANDARD_ASM */ + +/* standalone <linux/stdarg.h> exists */ +/* #undef HAVE_STANDALONE_LINUX_STDARG */ + +/* statx() is available */ +/* #undef HAVE_STATX */ + +/* STATX_MNT_ID is available */ +/* #undef HAVE_STATX_MNT_ID */ + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the 'strlcat' function. */ +#define HAVE_STRLCAT 1 + +/* Define to 1 if you have the 'strlcpy' function. */ +#define HAVE_STRLCPY 1 + +/* submit_bio is member of struct block_device_operations */ +/* #undef HAVE_SUBMIT_BIO_IN_BLOCK_DEVICE_OPERATIONS */ + +/* have super_block s_shrink */ +/* #undef HAVE_SUPER_BLOCK_S_SHRINK */ + +/* have super_block s_shrink pointer */ +/* #undef HAVE_SUPER_BLOCK_S_SHRINK_PTR */ + +/* have super_block s_wb_err */ +/* #undef HAVE_SUPER_BLOCK_S_WB_ERR */ + +/* sync_blockdev() is declared in include/blkdev.h */ +/* #undef HAVE_SYNC_BLOCKDEV */ + +/* struct kobj_type has default_groups */ +/* #undef HAVE_SYSFS_DEFAULT_GROUPS */ + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* timer_delete_sync is available */ +/* #undef HAVE_TIMER_DELETE_SYNC */ + +/* i_op->tmpfile() uses old dentry signature */ +/* #undef HAVE_TMPFILE_DENTRY */ + +/* i_op->tmpfile() has mnt_idmap */ +/* #undef HAVE_TMPFILE_IDMAP */ + +/* i_op->tmpfile() has userns */ +/* #undef HAVE_TMPFILE_USERNS */ + +/* totalhigh_pages() exists */ +/* #undef HAVE_TOTALHIGH_PAGES */ + +/* kernel has totalram_pages() */ +/* #undef HAVE_TOTALRAM_PAGES_FUNC */ + +/* Define to 1 if you have the 'udev_device_get_is_initialized' function. */ +/* #undef HAVE_UDEV_DEVICE_GET_IS_INITIALIZED */ + +/* kernel has __kernel_fpu_* functions */ +/* #undef HAVE_UNDERSCORE_KERNEL_FPU */ + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* iops->getattr() takes struct user_namespace* */ +/* #undef HAVE_USERNS_IOPS_GETATTR */ + +/* iops->setattr() takes struct user_namespace* */ +/* #undef HAVE_USERNS_IOPS_SETATTR */ + +#ifdef __amd64__ +/* Define if host toolchain supports VAES */ +#define HAVE_VAES 1 +#endif + +/* fops->clone_file_range() is available */ +/* #undef HAVE_VFS_CLONE_FILE_RANGE */ + +/* fops->dedupe_file_range() is available */ +/* #undef HAVE_VFS_DEDUPE_FILE_RANGE */ + +/* filemap_dirty_folio exists */ +/* #undef HAVE_VFS_FILEMAP_DIRTY_FOLIO */ + +/* generic_copy_file_range() is available */ +/* #undef HAVE_VFS_GENERIC_COPY_FILE_RANGE */ + +/* migratepage exists */ +/* #undef HAVE_VFS_MIGRATEPAGE */ + +/* migrate_folio exists */ +/* #undef HAVE_VFS_MIGRATE_FOLIO */ + +/* vfs_parse_fs_string() takes 3 args */ +/* #undef HAVE_VFS_PARSE_FS_STRING_3ARGS */ + +/* vfs_path_lookup() is exported */ +/* #undef HAVE_VFS_PATH_LOOKUP_EXPORTED */ + +/* address_space_operations->readpages exists */ +/* #undef HAVE_VFS_READPAGES */ + +/* read_folio exists */ +/* #undef HAVE_VFS_READ_FOLIO */ + +/* fops->remap_file_range() is available */ +/* #undef HAVE_VFS_REMAP_FILE_RANGE */ + +/* __set_page_dirty_nobuffers exists */ +/* #undef HAVE_VFS_SET_PAGE_DIRTY_NOBUFFERS */ + +/* splice_copy_file_range() is available */ +/* #undef HAVE_VFS_SPLICE_COPY_FILE_RANGE */ + +/* address_space_operations->writepage exists */ +/* #undef HAVE_VFS_WRITEPAGE */ + +/* __vmalloc page flags exists */ +/* #undef HAVE_VMALLOC_PAGE_KERNEL */ + +#ifdef __amd64__ +/* Define if host toolchain supports VPCLMULQDQ */ +#define HAVE_VPCLMULQDQ 1 +#endif + +/* int (*writepage_t)() takes struct folio* */ +/* #undef HAVE_WRITEPAGE_T_FOLIO */ + +/* write_cache_pages() is available */ +/* #undef HAVE_WRITE_CACHE_PAGES */ + +/* xattr_handler->get() wants dentry and inode and flags */ +/* #undef HAVE_XATTR_GET_DENTRY_INODE_FLAGS */ + +/* xattr_handler->set() wants both dentry and inode */ +/* #undef HAVE_XATTR_SET_DENTRY_INODE */ + +/* xattr_handler->set() takes mnt_idmap */ +/* #undef HAVE_XATTR_SET_IDMAP */ + +/* xattr_handler->set() takes user_namespace */ +/* #undef HAVE_XATTR_SET_USERNS */ + +/* Define if host toolchain supports XSAVE */ +#define HAVE_XSAVE 1 + +/* Define if host toolchain supports XSAVEOPT */ +#define HAVE_XSAVEOPT 1 + +/* Define if host toolchain supports XSAVES */ +#define HAVE_XSAVES 1 + +/* ZERO_PAGE() is GPL-only */ +/* #undef HAVE_ZERO_PAGE_GPL_ONLY */ + +/* Define if you have [z] */ +#define HAVE_ZLIB 1 + +/* libunwind is llvm libunwind */ +/* #undef IS_LIBUNWIND_LLVM */ + +/* kernel exports FPU functions */ +/* #undef KERNEL_EXPORTS_X86_FPU */ + +/* TBD: fetch(3) support */ +#if 0 +/* whether the chosen libfetch is to be loaded at run-time */ +#define LIBFETCH_DYNAMIC 1 + +/* libfetch is fetch(3) */ +#define LIBFETCH_IS_FETCH 1 + +/* libfetch is libcurl */ +#define LIBFETCH_IS_LIBCURL 0 + +/* soname of chosen libfetch */ +#define LIBFETCH_SONAME "libfetch.so.6" +#endif + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* make_request_fn() return type */ +/* #undef MAKE_REQUEST_FN_RET */ + +/* using complete_and_exit() instead */ +/* #undef SPL_KTHREAD_COMPLETE_AND_EXIT */ + +/* Defined for legacy compatibility. */ +#define SPL_META_ALIAS ZFS_META_ALIAS + +/* Defined for legacy compatibility. */ +#define SPL_META_RELEASE ZFS_META_RELEASE + +/* Defined for legacy compatibility. */ +#define SPL_META_VERSION ZFS_META_VERSION + +/* pde_data() is PDE_DATA() */ +/* #undef SPL_PDE_DATA */ + +/* Define to 1 if all of the C89 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define SYSTEM_FREEBSD 1 + +/* True if ZFS is to be compiled for a Linux system */ +/* #undef SYSTEM_LINUX */ + +/* Version number of package */ +/* #undef ZFS_DEBUG */ + +/* /dev/zfs minor */ +/* #undef ZFS_DEVICE_MINOR */ + +/* Define the project alias string. */ +#define ZFS_META_ALIAS "zfs-2.4.4-1-FreeBSD_g71a9f9578" + +/* Define the project author. */ +#define ZFS_META_AUTHOR "OpenZFS" + +/* Define the project release date. */ +/* #undef ZFS_META_DATA */ + +/* Define the maximum compatible kernel version. */ +#define ZFS_META_KVER_MAX "7.2" + +/* Define the minimum compatible kernel version. */ +#define ZFS_META_KVER_MIN "4.18" + +/* Define the project license. */ +#define ZFS_META_LICENSE "CDDL" + +/* Define the libtool library 'age' version information. */ +/* #undef ZFS_META_LT_AGE */ + +/* Define the libtool library 'current' version information. */ +/* #undef ZFS_META_LT_CURRENT */ + +/* Define the libtool library 'revision' version information. */ +/* #undef ZFS_META_LT_REVISION */ + +/* Define the project name. */ +#define ZFS_META_NAME "zfs" + +/* Define the project release. */ +#define ZFS_META_RELEASE "1-FreeBSD_g71a9f9578" + +/* Define the project version. */ +#define ZFS_META_VERSION "2.4.4" + +/* count is located in percpu_ref.data */ +/* #undef ZFS_PERCPU_REF_COUNT_IN_DATA */ diff --git a/sys/module/zfs/zfs_gitrev.h b/sys/module/zfs/zfs_gitrev.h new file mode 100644 index 000000000000..3fe7b414db24 --- /dev/null +++ b/sys/module/zfs/zfs_gitrev.h @@ -0,0 +1 @@ +#define ZFS_META_GITREV "zfs-2.4.4-0-g71a9f9578" |
