From 2c79a2fa6a423cb7af11536fb365921e4cbffb6b Mon Sep 17 00:00:00 2001 From: "Ricardo M. Correia" Date: Wed, 10 Mar 2010 12:03:48 -0800 Subject: [PATCH] Fix a few zdb bugs when trying to open a pool Specifically, the following bugs are fixed in this patch: 1) zdb wasn't getting the correct device size when the vdev is a block device. In Solaris, fstat64() returns the device size but in Linux an ioctl() is needed. 2) We were opening block devices with O_DIRECT, which caused pread64() to fail with EINVAL due to memory alignment issues. This was fixed by the previous umem cache alignment fix in stub implementation to align objects correctly. But we still needed to add a check for the error here. 3) We also make sure that we don't try to open a block device in write mode in userspace. This shouldn't happen, because zdb opens devices in read-only mode, and ztest only uses files. --- lib/libzpool/kernel.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c index 5f767e5949..d65c90a731 100644 --- a/lib/libzpool/kernel.c +++ b/lib/libzpool/kernel.c @@ -37,6 +37,7 @@ #include #include #include +#include /* for BLKGETSIZE64 */ #include /* @@ -552,13 +553,13 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3) return (errno); } -#ifdef __linux__ if (!(flags & FCREAT) && S_ISBLK(st.st_mode)) { +#ifdef __linux__ flags |= O_DIRECT; - if (flags & FWRITE) - flags |= O_EXCL; - } #endif + /* We shouldn't be writing to block devices in userspace */ + VERIFY(!(flags & FWRITE)); + } if (flags & FCREAT) old_umask = umask(0); @@ -581,6 +582,16 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3) return (err); } +#ifdef __linux__ + /* In Linux, use an ioctl to get the size of a block device. */ + if (S_ISBLK(st.st_mode)) { + if (ioctl(fd, BLKGETSIZE64, &st.st_size) != 0) { + err = errno; + close(fd); + return (err); + } + } +#endif (void) fcntl(fd, F_SETFD, FD_CLOEXEC); *vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL); @@ -634,6 +645,16 @@ vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset, } } +#ifdef __linux__ + if (rc == -1 && errno == EINVAL) { + /* + * Under Linux, this most likely means an alignment issue + * (memory or disk) due to O_DIRECT, so we abort() in order to + * catch the offender. + */ + abort(); + } +#endif if (rc == -1) return (errno);