From 8a719b0cee8bb1cdfd81bb08c4be702bb8321a86 Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Fri, 22 May 2020 11:20:23 +0000 Subject: [PATCH] libprocstat: fix ZFS support First of all, znode_phys_t hasn't been used for storing file attributes for a long time now. Modern ZFS versions use a System Attribute table with a flexible layout. But more importantly all the required information is available in znode_t itself. It's not easy to include zfs_znode.h in userland without breaking code because the most interesting parts of the header are kernel-only. And hardcoding field offsets is too fragile. So, I created a new compilation unit that includes zfs_znode.h using some mild kludges to get it and its dependencies to compile in userland. The compilation unit exports interesting field offsets and does not have any other code. PR: 194117 Reviewed by: markj MFC after: 2 weeks Sponsored by: Panzura Differential Revision: https://reviews.freebsd.org/D24941 --- lib/libprocstat/Makefile | 19 +++++---- lib/libprocstat/zfs.c | 82 +++++++++++++----------------------- lib/libprocstat/zfs/Makefile | 6 +-- lib/libprocstat/zfs_defs.c | 59 ++++++++++++++++++++++++++ lib/libprocstat/zfs_defs.h | 38 +++++++++++++++++ 5 files changed, 139 insertions(+), 65 deletions(-) create mode 100644 lib/libprocstat/zfs_defs.c create mode 100644 lib/libprocstat/zfs_defs.h diff --git a/lib/libprocstat/Makefile b/lib/libprocstat/Makefile index 46c2c8753990..007db5a585ee 100644 --- a/lib/libprocstat/Makefile +++ b/lib/libprocstat/Makefile @@ -57,16 +57,17 @@ MLINKS+=libprocstat.3 procstat_close.3 \ # XXX This is a hack. .if ${MK_CDDL} != "no" CFLAGS+= -DLIBPROCSTAT_ZFS -OBJS+= zfs/zfs.o -SOBJS+= zfs/zfs.pico -POBJS+= zfs/zfs.po +SRCS+= zfs.c +OBJS+= zfs/zfs_defs.o +SOBJS+= zfs/zfs_defs.pico +POBJS+= zfs/zfs_defs.po SUBDIR= zfs -zfs/zfs.o: .PHONY - @cd ${.CURDIR}/zfs && ${MAKE} zfs.o -zfs/zfs.pico: .PHONY - @cd ${.CURDIR}/zfs && ${MAKE} zfs.pico -zfs/zfs.po: .PHONY - @cd ${.CURDIR}/zfs && ${MAKE} zfs.po +zfs/zfs_defs.o: .PHONY + @cd ${.CURDIR}/zfs && ${MAKE} zfs_defs.o +zfs/zfs_defs.pico: .PHONY + @cd ${.CURDIR}/zfs && ${MAKE} zfs_defs.pico +zfs/zfs_defs.po: .PHONY + @cd ${.CURDIR}/zfs && ${MAKE} zfs_defs.po .endif .include diff --git a/lib/libprocstat/zfs.c b/lib/libprocstat/zfs.c index 8beb902fb7f0..066b0053a245 100644 --- a/lib/libprocstat/zfs.c +++ b/lib/libprocstat/zfs.c @@ -31,21 +31,12 @@ #include #define _KERNEL #include -#include #undef _KERNEL +#include +#include #include - -#undef lbolt -#undef lbolt64 -#undef gethrestime_sec -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include @@ -57,24 +48,15 @@ #define ZFS #include "libprocstat.h" #include "common_kvm.h" - -/* - * Offset calculations that are used to get data from znode without having the - * definition. - */ -#define LOCATION_ZID (2 * sizeof(void *)) -#define LOCATION_ZPHYS(zsize) ((zsize) - (2 * sizeof(void *) + sizeof(struct task))) +#include "zfs_defs.h" int zfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) { - znode_phys_t zphys; struct mount mount, *mountptr; - uint64_t *zid; - void *znodeptr, *vnodeptr; + void *znodeptr; char *dataptr; - void *zphys_addr; size_t len; int size; @@ -83,33 +65,27 @@ zfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) warnx("error getting sysctl"); return (1); } - znodeptr = malloc(size); - if (znodeptr == NULL) { + dataptr = malloc(size); + if (dataptr == NULL) { warnx("error allocating memory for znode storage"); return (1); } - /* Since we have problems including vnode.h, we'll use the wrappers. */ - vnodeptr = getvnodedata(vp); - if (!kvm_read_all(kd, (unsigned long)vnodeptr, znodeptr, - (size_t)size)) { - warnx("can't read znode at %p", (void *)vnodeptr); + + if ((size_t)size < offsetof_z_id + sizeof(uint64_t) || + (size_t)size < offsetof_z_mode + sizeof(mode_t) || + (size_t)size < offsetof_z_size + sizeof(uint64_t)) { + warnx("znode_t size is too small"); goto bad; } - /* - * z_id field is stored in the third pointer. We therefore skip the two - * first bytes. - * - * Pointer to the z_phys structure is the next last pointer. Therefore - * go back two bytes from the end. - */ - dataptr = znodeptr; - zid = (uint64_t *)(dataptr + LOCATION_ZID); - zphys_addr = *(void **)(dataptr + LOCATION_ZPHYS(size)); + if ((size_t)size != sizeof_znode_t) + warnx("znode_t size mismatch, data could be wrong"); - if (!kvm_read_all(kd, (unsigned long)zphys_addr, &zphys, - sizeof(zphys))) { - warnx("can't read znode_phys at %p", zphys_addr); + /* Since we have problems including vnode.h, we'll use the wrappers. */ + znodeptr = getvnodedata(vp); + if (!kvm_read_all(kd, (unsigned long)znodeptr, dataptr, + (size_t)size)) { + warnx("can't read znode at %p", (void *)znodeptr); goto bad; } @@ -119,18 +95,18 @@ zfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) warnx("can't read mount at %p", (void *)mountptr); goto bad; } - vn->vn_fsid = mount.mnt_stat.f_fsid.val[0]; - vn->vn_fileid = *zid; + /* - * XXX: Shows up wrong in output, but UFS has this error too. Could - * be that we're casting mode-variables from 64-bit to 8-bit or simply - * error in the mode-to-string function. + * XXX Assume that this is a znode, but it can be a special node + * under .zfs/. */ - vn->vn_mode = (mode_t)zphys.zp_mode; - vn->vn_size = (u_long)zphys.zp_size; - free(znodeptr); + vn->vn_fsid = mount.mnt_stat.f_fsid.val[0]; + vn->vn_fileid = *(uint64_t *)(void *)(dataptr + offsetof_z_id); + vn->vn_mode = *(mode_t *)(void *)(dataptr + offsetof_z_mode); + vn->vn_size = *(uint64_t *)(void *)(dataptr + offsetof_z_size); + free(dataptr); return (0); bad: - free(znodeptr); + free(dataptr); return (1); } diff --git a/lib/libprocstat/zfs/Makefile b/lib/libprocstat/zfs/Makefile index 1206b6a8a67c..880118fbc643 100644 --- a/lib/libprocstat/zfs/Makefile +++ b/lib/libprocstat/zfs/Makefile @@ -2,20 +2,20 @@ .PATH: ${.CURDIR:H} -SRCS= zfs.c -OBJS= zfs.o +SRCS= zfs_defs.c +OBJS= zfs_defs.o WARNS?= 1 CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem -CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head CFLAGS+= -I${.CURDIR:H} CFLAGS+= -DNEED_SOLARIS_BOOLEAN +CFLAGS+= -fno-builtin -nostdlib all: ${OBJS} CLEANFILES= ${OBJS} diff --git a/lib/libprocstat/zfs_defs.c b/lib/libprocstat/zfs_defs.c new file mode 100644 index 000000000000..c145eb9a3cf7 --- /dev/null +++ b/lib/libprocstat/zfs_defs.c @@ -0,0 +1,59 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Andriy Gapon + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* Pretend we are kernel to get the same binary layout. */ +#define _KERNEL + +/* A hack to deal with kpilite.h. */ +#define KLD_MODULE + +/* + * Prevent some headers from getting included and fake some types + * in order to allow this file to compile without bringing in + * too many kernel build dependencies. + */ +#define _OPENSOLARIS_SYS_PATHNAME_H_ +#define _OPENSOLARIS_SYS_POLICY_H_ +#define _OPENSOLARIS_SYS_VNODE_H_ +#define _VNODE_PAGER_ + +typedef struct vnode vnode_t; +typedef struct vattr vattr_t; +typedef struct xvattr xvattr_t; +typedef struct vsecattr vsecattr_t; +typedef enum vtype vtype_t; + +#include +#include + +size_t sizeof_znode_t = sizeof(znode_t); +size_t offsetof_z_id = offsetof(znode_t, z_id); +size_t offsetof_z_size = offsetof(znode_t, z_size); +size_t offsetof_z_mode = offsetof(znode_t, z_mode); diff --git a/lib/libprocstat/zfs_defs.h b/lib/libprocstat/zfs_defs.h new file mode 100644 index 000000000000..40c2e4d6e7e4 --- /dev/null +++ b/lib/libprocstat/zfs_defs.h @@ -0,0 +1,38 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Andriy Gapon + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LIBPROCSTAT_ZFS_DEFS_H +#define _LIBPROCSTAT_ZFS_DEFS_H + +extern size_t sizeof_znode_t; +extern size_t offsetof_z_id; +extern size_t offsetof_z_size; +extern size_t offsetof_z_mode; + +#endif /* _LIBPROCSTAT_ZFS_DEFS_H */