mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 19:34:43 +01:00
libzfs: don't truncate a resolved vdev path in zpool_vdev_name()
zpool_vdev_name() copied the result of realpath() into a 64 byte stack buffer shared with the short formatted names, so a vdev whose resolved path was longer than 63 bytes was reported cut short by zpool status -L and anything else asking for VDEV_NAME_FOLLOW_LINKS. The cut is made at a byte boundary, so a multi-byte character straddling it is left as invalid UTF-8, which also makes zpool status -j -L emit JSON a parser rejects. Resolve straight into a buffer of the right size. realpath() fills a caller supplied buffer of at least PATH_MAX, as it is used elsewhere in libzutil, which also removes the intermediate allocation. The other three users of that buffer format a guid, a raidz name, or a draid name, and none of them can exceed its length. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Michael Heller <[email protected]> Closes #18851 Closes #18871
This commit is contained in:
1 parent
be62c53850
commit
74e76f2d80
4 files changed
+94
-6
No files matched your search
@@ -4740,6 +4740,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
|
||||
@@ -4765,12 +4766,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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -594,6 +594,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']
|
||||
|
||||
|
||||
@@ -1386,6 +1386,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 \
|
||||
|
||||
+89
@@ -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"
|
||||
Reference in new issue
Block a user