Skip to content

Commit 283c97b

Browse files
committed
Detect BTX boot loader in the zfs reserved boot section
Signed-off-by: Don Brady <[email protected]>
1 parent ca80cdc commit 283c97b

File tree

11 files changed

+219
-6
lines changed

11 files changed

+219
-6
lines changed

include/sys/vdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ extern void vdev_label_write(zio_t *zio, vdev_t *vd, int l, abd_t *buf, uint64_t
209209
extern int vdev_label_read_bootenv(vdev_t *, nvlist_t *);
210210
extern int vdev_label_write_bootenv(vdev_t *, nvlist_t *);
211211
extern int vdev_uberblock_sync_list(vdev_t **, int, struct uberblock *, int);
212+
extern int vdev_check_boot_reserve(spa_t *, vdev_t *);
212213

213214
typedef enum {
214215
VDEV_LABEL_CREATE, /* create/add a new device */

lib/libzfs/libzfs_pool.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,8 +3520,8 @@ zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk,
35203520
break;
35213521

35223522
case EBUSY:
3523-
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3524-
"%s is busy"), new_disk);
3523+
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy"),
3524+
new_disk);
35253525
(void) zfs_error(hdl, EZFS_BADDEV, errbuf);
35263526
break;
35273527

@@ -3561,9 +3561,23 @@ zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk,
35613561
"being replaced"));
35623562
(void) zfs_error(hdl, EZFS_BADDEV, errbuf);
35633563
break;
3564+
} else {
3565+
(void) zpool_standard_error(hdl, errno, errbuf);
35643566
}
3565-
/* fall through */
3567+
break;
35663568

3569+
case EADDRINUSE:
3570+
/*
3571+
* The boot reserved area is already being used (FreeBSD)
3572+
*/
3573+
if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
3574+
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3575+
"the reserved boot area needed for the expansion "
3576+
"is already being used by a boot loader"));
3577+
} else {
3578+
(void) zpool_standard_error(hdl, errno, errbuf);
3579+
}
3580+
break;
35673581
default:
35683582
(void) zpool_standard_error(hdl, errno, errbuf);
35693583
}

lib/libzpool/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ nodist_libzpool_la_SOURCES = \
4343
module/os/linux/zfs/arc_os.c \
4444
module/os/linux/zfs/trace.c \
4545
module/os/linux/zfs/vdev_file.c \
46+
module/os/linux/zfs/vdev_label_os.c \
4647
module/os/linux/zfs/zfs_debug.c \
4748
module/os/linux/zfs/zfs_racct.c \
4849
module/os/linux/zfs/zfs_znode.c \

module/Kbuild.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ ZFS_OBJS_OS := \
445445
trace.o \
446446
vdev_disk.o \
447447
vdev_file.o \
448+
vdev_label_os.o \
448449
zfs_acl.o \
449450
zfs_ctldir.o \
450451
zfs_debug.o \

module/os/freebsd/zfs/vdev_label_os.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,62 @@ vdev_label_write_pad2(vdev_t *vd, const char *buf, size_t size)
7272
abd_free(pad2);
7373
return (error);
7474
}
75+
76+
static void
77+
vdev_child_done(zio_t *zio)
78+
{
79+
zio_t *pio = zio->io_private;
80+
81+
mutex_enter(&pio->io_lock);
82+
pio->io_error = zio_worst_error(pio->io_error, zio->io_error);
83+
mutex_exit(&pio->io_lock);
84+
}
85+
86+
/*
87+
* Check if the reserved boot area is in-use.
88+
*
89+
* When booting FreeBSD with an MBR partition with ZFS, the zfsboot file
90+
* (which understands the ZFS file system) is written to the ZFS BOOT
91+
* reserve area (at offset 512K). We check for that here before attaching
92+
* a disk to raidz which would then corrupt this boot data.
93+
*/
94+
int
95+
vdev_check_boot_reserve(spa_t *spa, vdev_t *childvd)
96+
{
97+
ASSERT(childvd->vdev_ops->vdev_op_leaf);
98+
99+
size_t size = SPA_MINBLOCKSIZE;
100+
abd_t *abd = abd_alloc_linear(size, B_FALSE);
101+
102+
zio_t *pio = zio_root(spa, NULL, NULL, 0);
103+
/*
104+
* Note: zio_vdev_child_io() adds VDEV_LABEL_START_SIZE to the offset
105+
* to calculate the physical offset to write to. Passing in a negative
106+
* offset lets us access the boot area.
107+
*/
108+
zio_nowait(zio_vdev_child_io(pio, NULL, childvd,
109+
VDEV_BOOT_OFFSET - VDEV_LABEL_START_SIZE, abd, size, ZIO_TYPE_READ,
110+
ZIO_PRIORITY_ASYNC_READ, 0, vdev_child_done, pio));
111+
zio_wait(pio);
112+
113+
unsigned char *buf = abd_to_buf(abd);
114+
115+
/*
116+
* The BTX server has a special header at the begining.
117+
*
118+
* btx_hdr: .byte 0xeb # Machine ID
119+
* .byte 0xe # Header size
120+
* .ascii "BTX" # Magic
121+
* .byte 0x1 # Major version
122+
* .byte 0x2 # Minor version
123+
* .byte BTX_FLAGS # Flags
124+
*/
125+
if (buf[0] == 0xeb && buf[1] == 0x0e &&
126+
buf[2] == 'B' && buf[3] == 'T' && buf[4] == 'X') {
127+
abd_free(abd);
128+
return (EBUSY);
129+
}
130+
131+
abd_free(abd);
132+
return (0);
133+
}

module/os/linux/zfs/vdev_label_os.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* CDDL HEADER START
3+
*
4+
* The contents of this file are subject to the terms of the
5+
* Common Development and Distribution License (the "License").
6+
* You may not use this file except in compliance with the License.
7+
*
8+
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9+
* or https://opensource.org/licenses/CDDL-1.0.
10+
* See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*
13+
* When distributing Covered Code, include this CDDL HEADER in each
14+
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15+
* If applicable, add the following below this CDDL HEADER, with the
16+
* fields enclosed by brackets "[]" replaced with your own identifying
17+
* information: Portions Copyright [yyyy] [name of copyright owner]
18+
*
19+
* CDDL HEADER END
20+
*/
21+
22+
/*
23+
* Copyright (c) 2023 by iXsystems, Inc.
24+
*/
25+
26+
#include <sys/zfs_context.h>
27+
#include <sys/spa.h>
28+
#include <sys/spa_impl.h>
29+
#include <sys/vdev.h>
30+
#include <sys/vdev_impl.h>
31+
32+
/*
33+
* Check if the reserved boot area is in-use.
34+
*
35+
* Not aware of any external uses on Linux.
36+
*/
37+
int
38+
vdev_check_boot_reserve(spa_t *spa, vdev_t *childvd)
39+
{
40+
(void) spa;
41+
(void) childvd;
42+
43+
return (0);
44+
}

module/zfs/spa.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7059,6 +7059,12 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing,
70597059
return (spa_vdev_exit(spa, newrootvd, txg,
70607060
ENXIO));
70617061
}
7062+
/* Also fail if reserved boot area is in-use */
7063+
if (vdev_check_boot_reserve(spa, oldvd->vdev_child[i])
7064+
!= 0) {
7065+
return (spa_vdev_exit(spa, newrootvd, txg,
7066+
EADDRINUSE));
7067+
}
70627068
}
70637069
}
70647070

module/zfs/vdev_raidz.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
* context. The design also allows for fast discovery of what data to copy.
208208
*
209209
* The VDEV metaslabs are processed, one at a time, to copy the block data to
210-
* have it flow across all the disks. The metasab is disabled for allocations
210+
* have it flow across all the disks. The metaslab is disabled for allocations
211211
* during the copy. As an optimization, we only copy the allocated data which
212212
* can be determined by looking at the metaslab range tree. During the copy we
213213
* must maintain the redundancy guarantees of the RAIDZ VDEV (e.g. parity count
@@ -267,7 +267,7 @@
267267
*
268268
* == Reflow Progress Updates ==
269269
* After the initial scratch-based reflow, the expansion process works
270-
* similarly to device removal. We create a new open context thread whichi
270+
* similarly to device removal. We create a new open context thread which
271271
* reflows the data, and periodically kicks off sync tasks to update logical
272272
* state. In this case, state is the committed progress (offset of next data
273273
* to copy). We need to persist the completed offset on disk, so that if we

tests/runfiles/common.run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ tags = ['functional', 'redacted_send']
772772
tests = ['raidz_001_neg', 'raidz_002_pos', 'raidz_003_pos', 'raidz_004_pos',
773773
'raidz_expand_001_pos', 'raidz_expand_002_pos', 'raidz_expand_003_neg',
774774
'raidz_expand_003_pos', 'raidz_expand_004_pos', 'raidz_expand_005_pos',
775-
'raidz_expand_006_neg']
775+
'raidz_expand_006_neg', 'raidz_expand_007_neg']
776776
tags = ['functional', 'raidz']
777777
timeout = 1200
778778

tests/zfs-tests/tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
16691669
functional/raidz/raidz_expand_004_pos.ksh \
16701670
functional/raidz/raidz_expand_005_pos.ksh \
16711671
functional/raidz/raidz_expand_006_neg.ksh \
1672+
functional/raidz/raidz_expand_007_neg.ksh \
16721673
functional/raidz/setup.ksh \
16731674
functional/redacted_send/cleanup.ksh \
16741675
functional/redacted_send/redacted_compressed.ksh \
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/ksh -p
2+
#
3+
# CDDL HEADER START
4+
#
5+
# The contents of this file are subject to the terms of the
6+
# Common Development and Distribution License (the "License").
7+
# You may not use this file except in compliance with the License.
8+
#
9+
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10+
# or http://www.opensolaris.org/os/licensing.
11+
# See the License for the specific language governing permissions
12+
# and limitations under the License.
13+
#
14+
# When distributing Covered Code, include this CDDL HEADER in each
15+
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16+
# If applicable, add the following below this CDDL HEADER, with the
17+
# fields enclosed by brackets "[]" replaced with your own identifying
18+
# information: Portions Copyright [yyyy] [name of copyright owner]
19+
#
20+
# CDDL HEADER END
21+
#
22+
23+
#
24+
# Copyright (c) 2023 by iXsystems, Inc.
25+
#
26+
27+
. $STF_SUITE/include/libtest.shlib
28+
29+
#
30+
# DESCRIPTION:
31+
# Negative for FreeBSD Only
32+
#
33+
# Attempting to expand a RAIDZ should fail if the scratch area on the
34+
# existing disks contains BTX Server binary (used to boot FreeBSD when
35+
# using MBR partitions with ZFS).
36+
#
37+
# STRATEGY:
38+
# 1. Create raidz pool
39+
# 2. Add a BTX header to the reserved boot area
40+
# 3. Attempt to attach a device to the raidz vdev
41+
# 4. Verify that device attached failed
42+
# 5. Destroy the raidz pool
43+
44+
typeset -r devs=4
45+
typeset -r dev_size_mb=128
46+
typeset -a disks
47+
48+
function cleanup
49+
{
50+
log_pos zpool status "$TESTPOOL"
51+
52+
poolexists "$TESTPOOL" && log_must_busy zpool destroy "$TESTPOOL"
53+
54+
for i in {0..$devs}; do
55+
log_must rm -f "$TEST_BASE_DIR/dev-$i"
56+
done
57+
}
58+
59+
log_onexit cleanup
60+
61+
for i in {0..$devs}; do
62+
device=$TEST_BASE_DIR/dev-$i
63+
# simulate active BTX Server data by inserting a BTX header
64+
printf "\xeb\x0e%s\x01\x02\x80" "BTX" | dd of="$device" \
65+
bs=512 seek=1024 status=none
66+
log_must truncate -s ${dev_size_mb}M "$device"
67+
if [[ $i -ne $devs ]]; then
68+
disks[${#disks[*]}+1]=$device
69+
fi
70+
done
71+
72+
log_must zpool create -f -o cachefile=none "$TESTPOOL" raidz1 "${disks[@]}"
73+
74+
if is_freebsd; then
75+
# expecting attach to fail
76+
log_mustnot_expect "the reserved boot area" zpool attach -f \
77+
"$TESTPOOL" raidz1-0 "$TEST_BASE_DIR/dev-$devs"
78+
log_must zpool destroy "$TESTPOOL"
79+
log_pass "raidz attach failed with in-use reserved boot area"
80+
else
81+
# expecting attach to pass everywhere else
82+
log_must zpool attach -f "$TESTPOOL" raidz1-0 "$TEST_BASE_DIR/dev-$devs"
83+
log_must zpool destroy "$TESTPOOL"
84+
log_pass "raidz attach passed with in-use reserved boot area"
85+
fi
86+

0 commit comments

Comments
 (0)