From 5bd332a683811586039f99f31c01d4f2f7181334 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Mon, 26 Jul 2021 15:21:18 +0100 Subject: [PATCH] cache, cow: Use full pread/pwrite operations Although it probably cannot happen on Linux, POSIX allows pread/pwrite to return or write fewer bytes than requested. The cache and cow filters didn't handle this situation. Replace the raw pread(2)/pwrite(2) syscalls with alternate versions which can handle this. (cherry picked from commit ce0db9d7736dd28dd0f10951ce65853e50b35e41) --- common/utils/Makefile.am | 1 + common/utils/full-rw.c | 81 ++++++++++++++++++++++++++++++++++++++++ common/utils/utils.h | 2 + filters/cache/blk.c | 10 ++--- filters/cow/blk.c | 6 +-- 5 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 common/utils/full-rw.c diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 1708a4c8..14e9dfc4 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -40,6 +40,7 @@ libutils_la_SOURCES = \ cleanup-nbdkit.c \ cleanup.h \ environ.c \ + full-rw.c \ quote.c \ utils.c \ utils.h \ diff --git a/common/utils/full-rw.c b/common/utils/full-rw.c new file mode 100644 index 00000000..55b32cdd --- /dev/null +++ b/common/utils/full-rw.c @@ -0,0 +1,81 @@ +/* nbdkit + * Copyright (C) 2021 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT 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 RED HAT 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. + */ + +/* These functions are like pread(2)/pwrite(2) but they always read or + * write the full amount, or fail. + */ + +#include + +#include +#include +#include +#include + +ssize_t +full_pread (int fd, void *buf, size_t count, off_t offset) +{ + ssize_t ret = 0, r; + + while (count > 0) { + r = pread (fd, buf, count, offset); + if (r == -1) return -1; + if (r == 0) { + /* Presumably the caller wasn't expecting end-of-file here, so + * return an error. + */ + errno = EIO; + return -1; + } + ret += r; + offset += r; + count -= r; + } + + return ret; +} + +ssize_t +full_pwrite (int fd, const void *buf, size_t count, off_t offset) +{ + ssize_t ret = 0, r; + + while (count > 0) { + r = pwrite (fd, buf, count, offset); + if (r == -1) return -1; + ret += r; + offset += r; + count -= r; + } + + return ret; +} diff --git a/common/utils/utils.h b/common/utils/utils.h index f8f70212..83397ae1 100644 --- a/common/utils/utils.h +++ b/common/utils/utils.h @@ -40,5 +40,7 @@ extern int set_cloexec (int fd); extern int set_nonblock (int fd); extern char **copy_environ (char **env, ...) __attribute__((__sentinel__)); extern char *make_temporary_directory (void); +extern ssize_t full_pread (int fd, void *buf, size_t count, off_t offset); +extern ssize_t full_pwrite (int fd, const void *buf, size_t count, off_t offset); #endif /* NBDKIT_UTILS_H */ diff --git a/filters/cache/blk.c b/filters/cache/blk.c index f85ada35..42bd3779 100644 --- a/filters/cache/blk.c +++ b/filters/cache/blk.c @@ -250,7 +250,7 @@ _blk_read_multiple (nbdkit_next *next, " (offset %" PRIu64 ")", blknum, (uint64_t) offset); - if (pwrite (fd, block, blksize * runblocks, offset) == -1) { + if (full_pwrite (fd, block, blksize * runblocks, offset) == -1) { *err = errno; nbdkit_error ("pwrite: %m"); return -1; @@ -262,7 +262,7 @@ _blk_read_multiple (nbdkit_next *next, } } else { /* Read cache. */ - if (pread (fd, block, blksize * runblocks, offset) == -1) { + if (full_pread (fd, block, blksize * runblocks, offset) == -1) { *err = errno; nbdkit_error ("pread: %m"); return -1; @@ -339,7 +339,7 @@ blk_cache (nbdkit_next *next, nbdkit_debug ("cache: cache block %" PRIu64 " (offset %" PRIu64 ")", blknum, (uint64_t) offset); - if (pwrite (fd, block, blksize, offset) == -1) { + if (full_pwrite (fd, block, blksize, offset) == -1) { *err = errno; nbdkit_error ("pwrite: %m"); return -1; @@ -380,7 +380,7 @@ blk_writethrough (nbdkit_next *next, nbdkit_debug ("cache: writethrough block %" PRIu64 " (offset %" PRIu64 ")", blknum, (uint64_t) offset); - if (pwrite (fd, block, blksize, offset) == -1) { + if (full_pwrite (fd, block, blksize, offset) == -1) { *err = errno; nbdkit_error ("pwrite: %m"); return -1; @@ -414,7 +414,7 @@ blk_write (nbdkit_next *next, nbdkit_debug ("cache: writeback block %" PRIu64 " (offset %" PRIu64 ")", blknum, (uint64_t) offset); - if (pwrite (fd, block, blksize, offset) == -1) { + if (full_pwrite (fd, block, blksize, offset) == -1) { *err = errno; nbdkit_error ("pwrite: %m"); return -1; diff --git a/filters/cow/blk.c b/filters/cow/blk.c index 4ec8d1b8..121b0dd4 100644 --- a/filters/cow/blk.c +++ b/filters/cow/blk.c @@ -278,7 +278,7 @@ blk_read_multiple (nbdkit_next *next, memset (block + n, 0, tail); } else if (state == BLOCK_ALLOCATED) { /* Read overlay. */ - if (pread (fd, block, BLKSIZE * runblocks, offset) == -1) { + if (full_pread (fd, block, BLKSIZE * runblocks, offset) == -1) { *err = errno; nbdkit_error ("pread: %m"); return -1; @@ -353,7 +353,7 @@ blk_cache (nbdkit_next *next, memset (block + n, 0, tail); if (mode == BLK_CACHE_COW) { - if (pwrite (fd, block, BLKSIZE, offset) == -1) { + if (full_pwrite (fd, block, BLKSIZE, offset) == -1) { *err = errno; nbdkit_error ("pwrite: %m"); return -1; @@ -372,7 +372,7 @@ blk_write (uint64_t blknum, const uint8_t *block, int *err) nbdkit_debug ("cow: blk_write block %" PRIu64 " (offset %" PRIu64 ")", blknum, (uint64_t) offset); - if (pwrite (fd, block, BLKSIZE, offset) == -1) { + if (full_pwrite (fd, block, BLKSIZE, offset) == -1) { *err = errno; nbdkit_error ("pwrite: %m"); return -1; -- 2.31.1